ch4mpy / spring-addons

Ease spring OAuth2 resource-servers configuration and testing
Apache License 2.0
521 stars 84 forks source link

POST /logout response Forbidden 403 #179

Closed TheNullablePrototype closed 7 months ago

TheNullablePrototype commented 7 months ago

Frontend (SPA / Vue.js 3)

Describe the bug I'm following the bff turorial, and it looks like there was a problem implementing the logout.

First I saw a warning about CORS policy: image

I'm not really sure if I did it right, but I did it like this: application.yaml (Gateway)

scheme: http
issuer: http://localhost:9090/realms/client
client-id: 'client-id'
client-secret: 'client-secret'
user-name-attribute: 'preferred_username'
gateway-uri: ${scheme}://localhost:${server.port}
api-uri: ${scheme}://localhost:8090/api
ui-uri: ${scheme}://localhost:1000

spring:
  security:
    oauth2:
      client:
        provider:
          keycloak:
            issuer-uri: ${issuer}
            user-name-attribute: ${user-name-attribute}
        registration:
          keycloak:
            provider: keycloak
            client-id: ${client-id}
            client-secret: ${client-secret}
            authorization-grant-type: authorization_code
            scope:
              - openid
              - profile
              - email
              - offline_access
              - roles
  cloud:
    gateway:
      default-filters:
        - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin
        - SaveSession
      routes:
        # set a redirection from / to the UI
        - id: home
          uri: ${ui-uri}
          predicates:
            - Path=/
          filters:
            - RedirectTo=301,${ui-uri}
        # BFF access to greetings API (with TokenRelay replacing session cookies with access tokens)
        # To be used by SPAs (Vue app in our case)
        - id: api-bff
          uri: ${api-uri}
          predicates:
            - Path=/bff/v1/**
          filters:
            - TokenRelay=
            - StripPrefix=2
        - id: ui
          uri: ${ui-uri}
          predicates:
            - Path=/ui/**

com:
  c4-soft:
    springaddons:
      oidc:
        ops:
          - iss: ${issuer}
            authorities:
              - path: $.realm_access.roles
            username-claim: ${user-name-attribute}
        client:
          client-uri: ${gateway-uri}
          security-matchers:
            - /login/**
            - /oauth2/**
            - /logout
            - /bff/**
          permit-all:
            - /login/**
            - /oauth2/**
            - /bff/**
          cors:
            - path: /bff/**
              allowed-origin-patterns:
                - ${gateway-uri}
                - https://localhost/
#       ======================================================== START "fix" CORS policy
            - path: /logout
              allowed-origin-patterns:
                - ${gateway-uri}
                - ${ui-uri}
                - https://localhost/
#       ======================================================== END "fix" CORS policy
          csrf: cookie-accessible-from-js
#          login-path: /ui/
#          post-login-redirect-path: /ui/
#          post-logout-redirect-path: /ui/
#          post-login-redirect-host: ${ui-uri}
          oauth2-redirections:
            rp-initiated-logout: NO_CONTENT

        resourceserver:
          permit-all:
            - /
            - /login-options
            - /ui/**
            - /actuator/health/readiness
            - /actuator/health/liveness
            - /favicon.ico
          cors:
            - path: /login-options
              allowed-origin-patterns:
                - ${gateway-uri}
                - ${ui-uri}
                - https://localhost/

After that, I started getting a new error when I requested it: The request is ended with a 403 FORBIDDEN code and the response is: An expected CSRF token cannot be found. This leads to NOT triggering the logout process for providers.

From example

const api = axios.create({
  transformRequest: [dateTransformer, ...axios.defaults.transformRequest],
  // withCredentials: true,
  timeout: 5000,
  headers: { 'Content-Type': 'application/json' }
})

// triggered on logout button
export const logout = async () => {
  return api.post('http://localhost:8080/logout').catch(handleCatch) 
}

As I understand it, cookies are not sent with this policy, how should I correctly implement the /logout request so that everything works "as it should"? image

ch4mpy commented 7 months ago

I can spot quite a few mistakes above:

CORS

If you correctly use a reverse-proxy serving both UI and BFF, CORS configuration is not needed.

You are apparently sending a request to your BFF (http://localhost:8080) with http://localhost:1000 as origin. This is wrong.

The reason why you need a reverse-proxy serving both UI assets and BFF is that Spring session cookies are flagged with SameSite=Lax by default. So, for the browser to send session cookie with Vue requests to the BFF (and give the TokenRelay filter an opportunity to do its job), Vue app & BFF should have the same origin (the reverse-proxy).

In the tutorial, there is a route for the UI which makes the BFF itself a reverse-proxy for the UI:

      - id: ui
        uri: ${ui-uri}
        predicates:
        - Path=/ui/**

This enough to achieve same origin. You should probably restore this route and point your browser to http://localhost:8080/ui instead of http://localhost:1000.

This requires to set next.config.js with:

const nextConfig = {
    basePath: '/ui',
    assetPrefix: '/ui',
}

You should also probably restore the post-login & post-logout properties to redirect to your UI through the reverse-proxy (the default for a Spring OAuth2 client is itself and redirecting to the BFF will be problematic)

CSRF

In the case where the HTTP client you use in Vue does not handle CSRF tokens transparently (like Angular one does), you have to read the XSRF-TOKEN value (at least one GET requests to the backend must have been done for this cookie to be set) and provide it as X-XSRF-TOKEN header to your POST, PUT, PATCH and DELETE requests.

TheNullablePrototype commented 7 months ago

Thanks for the answer, I will try to fix the errors and unsubscribe about the result. Is it necessary for my frontend application to have the /ui suffix? I use Vite to build without Nuxt and TS

ch4mpy commented 7 months ago

you need something for the reverse-proxy to know that a request is for a Vue asset and that it should be routed to the whatever serves it (Vue dev-server on your dev machine). A path prefix is the easiest solution. Refer to Vite doc for how to set a baseHref (called basePath in next.js, so it's probably the same for Vite), I never used Vite.

TheNullablePrototype commented 7 months ago

I restored the configurations according to the tutorial, set the frontend base path of the application /ui vite.config.mjs:

export default defineConfig({
  base: '/ui',
  // ...
  server: {
    proxy: {
      '/api': 'http://localhost:8080/bff/v1/'
    }
  },
  // ...
})

restored Gateway application.yaml:

scheme: http
issuer: http://localhost:9090/realms/client
client-id: 'client-id'
client-secret: 'client-secret'
user-name-attribute: 'preferred_username'
gateway-uri: ${scheme}://localhost:${server.port}
api-uri: ${scheme}://localhost:8090/api
ui-uri: ${scheme}://localhost:1000

spring:
  security:
    oauth2:
      client:
        provider:
          keycloak:
            issuer-uri: ${issuer}
            user-name-attribute: ${user-name-attribute}
        registration:
          keycloak:
            provider: keycloak
            client-id: ${client-id}
            client-secret: ${client-secret}
            authorization-grant-type: authorization_code
            scope:
              - openid
              - profile
              - email
              - offline_access
              - roles
  cloud:
    gateway:
      default-filters:
        - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin
        - SaveSession
      routes:
        # set a redirection from / to the UI
        - id: home
          uri: ${gateway-uri}
          predicates:
            - Path=/
          filters:
            - RedirectTo=301,${gateway-uri}/ui/
        # BFF access to greetings API (with TokenRelay replacing session cookies with access tokens)
        # To be used by SPAs (Vue app in our case)
        - id: api-bff
          uri: ${api-uri}
          predicates:
            - Path=/bff/v1/**
          filters:
            - TokenRelay=
            - StripPrefix=2
        - id: ui
          uri: ${ui-uri}
          predicates:
            - Path=/ui/**

com:
  c4-soft:
    springaddons:
      oidc:
        ops:
          - iss: ${issuer}
            authorities:
              - path: $.realm_access.roles
            username-claim: ${user-name-attribute}
        client:
          client-uri: ${gateway-uri}
          security-matchers:
            - /login/**
            - /oauth2/**
            - /logout
            - /bff/**
          permit-all:
            - /login/**
            - /oauth2/**
            - /bff/**
          cors:
            - path: /bff/**
              allowed-origin-patterns:
                - ${gateway-uri}
                - https://localhost/
          csrf: cookie-accessible-from-js
          login-path: /ui/
          post-login-redirect-path: /ui/
          post-logout-redirect-path: /ui/
          oauth2-redirections:
            rp-initiated-logout: NO_CONTENT
        resourceserver:
          permit-all:
            - /
            - /login-options
            - /ui/**
            - /actuator/health/readiness
            - /actuator/health/liveness
            - /favicon.ico
          cors:
            - path: /login-options
              allowed-origin-patterns:
                - ${gateway-uri}
                - https://localhost/

Current application behavior: frontend: browser -> localhost:1000 -> redirect -> localhost:1000/ui backend: browser -> localhost:8080 -> redirect -> localhost:8080/ui // route with id home redirect

BUT i got 500 server error when reach localhost:8080/ui : I really can't figure out why this is happening.

Whitelabel Error Page
This application has no configured error view, so you are seeing this as a fallback.

Sun Jan 21 21:09:11 MSK 2024
[07e90b85-16] There was an unexpected error (type=Internal Server Error, status=500).
Connection refused: getsockopt: localhost/127.0.0.1:1000
io.netty.channel.AbstractChannel$AnnotatedConnectException: Connection refused: getsockopt: localhost/127.0.0.1:1000
    Suppressed: The stacktrace has been enhanced by Reactor, refer to additional information below: 
Assembly trace from producer [reactor.core.publisher.MonoError] :
    reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:94)
    reactor.netty.transport.TransportConnector$MonoChannelPromise.tryFailure(TransportConnector.java:576)
Error has been observed at the following site(s):
    *__FluxOnErrorResume$ResumeSubscriber.onError ⇢ at reactor.netty.transport.TransportConnector$MonoChannelPromise.tryFailure(TransportConnector.java:576)
    *__________________________Mono.onErrorResume ⇢ at reactor.netty.transport.TransportConnector.lambda$connect$6(TransportConnector.java:166)
    *________________________________Mono.flatMap ⇢ at reactor.netty.transport.TransportConnector.connect(TransportConnector.java:165)
    *_________________________________Mono.create ⇢ at reactor.netty.resources.DefaultPooledConnectionProvider$PooledConnectionAllocator.connectChannel(DefaultPooledConnectionProvider.java:535)
    *_________________________________Mono.create ⇢ at reactor.netty.resources.PooledConnectionProvider.acquire(PooledConnectionProvider.java:126)
    *_________________________________Mono.create ⇢ at reactor.netty.http.client.HttpClientConnect$MonoHttpConnect.subscribe(HttpClientConnect.java:210)
    |_                             Mono.retryWhen ⇢ at reactor.netty.http.client.HttpClientConnect$MonoHttpConnect.subscribe(HttpClientConnect.java:275)
    *______________________________Flux.concatMap ⇢ at reactor.util.retry.RetrySpec.lambda$generateCompanion$6(RetrySpec.java:360)
    |_                           Flux.onErrorStop ⇢ at reactor.util.retry.RetrySpec.lambda$generateCompanion$6(RetrySpec.java:379)
    *________________________Flux.deferContextual ⇢ at reactor.util.retry.RetrySpec.generateCompanion(RetrySpec.java:357)
    *____________________________Mono.flatMapMany ⇢ at reactor.netty.http.client.HttpClientFinalizer.responseConnection(HttpClientFinalizer.java:102)
    *___________________________________Flux.then ⇢ at org.springframework.cloud.gateway.filter.NettyRoutingFilter.filter(NettyRoutingFilter.java:198)
    *__________________________________Mono.defer ⇢ at org.springframework.cloud.gateway.handler.FilteringWebHandler$DefaultGatewayFilterChain.filter(FilteringWebHandler.java:113)
    *__________________________________Mono.defer ⇢ at org.springframework.cloud.gateway.handler.FilteringWebHandler$DefaultGatewayFilterChain.filter(FilteringWebHandler.java:113)
    *__________________________________Mono.defer ⇢ at org.springframework.cloud.gateway.handler.FilteringWebHandler$DefaultGatewayFilterChain.filter(FilteringWebHandler.java:113)
    *__________________________________Mono.defer ⇢ at org.springframework.cloud.gateway.handler.FilteringWebHandler$DefaultGatewayFilterChain.filter(FilteringWebHandler.java:113)
    *___________________________________Mono.then ⇢ at org.springframework.cloud.gateway.filter.factory.SaveSessionGatewayFilterFactory$1.filter(SaveSessionGatewayFilterFactory.java:45)
    *__________________________________Mono.defer ⇢ at org.springframework.cloud.gateway.handler.FilteringWebHandler$DefaultGatewayFilterChain.filter(FilteringWebHandler.java:113)
    *___________________________________Mono.then ⇢ at org.springframework.cloud.gateway.filter.factory.DedupeResponseHeaderGatewayFilterFactory$1.filter(DedupeResponseHeaderGatewayFilterFactory.java:93)
    *__________________________________Mono.defer ⇢ at org.springframework.cloud.gateway.handler.FilteringWebHandler$DefaultGatewayFilterChain.filter(FilteringWebHandler.java:113)
    *__________________________________Mono.defer ⇢ at org.springframework.cloud.gateway.handler.FilteringWebHandler$DefaultGatewayFilterChain.filter(FilteringWebHandler.java:113)
    *___________________________________Mono.then ⇢ at org.springframework.cloud.gateway.filter.NettyWriteResponseFilter.filter(NettyWriteResponseFilter.java:69)
    |_                            Mono.doOnCancel ⇢ at org.springframework.cloud.gateway.filter.NettyWriteResponseFilter.filter(NettyWriteResponseFilter.java:101)
    |_                             Mono.doOnError ⇢ at org.springframework.cloud.gateway.filter.NettyWriteResponseFilter.filter(NettyWriteResponseFilter.java:102)
    *__________________________________Mono.defer ⇢ at org.springframework.cloud.gateway.handler.FilteringWebHandler$DefaultGatewayFilterChain.filter(FilteringWebHandler.java:113)
    *__________________________________Mono.defer ⇢ at org.springframework.cloud.gateway.handler.FilteringWebHandler$DefaultGatewayFilterChain.filter(FilteringWebHandler.java:113)
    |_                             Mono.doFinally ⇢ at org.springframework.cloud.gateway.filter.RemoveCachedBodyFilter.filter(RemoveCachedBodyFilter.java:35)
    *__________________________________Mono.defer ⇢ at org.springframework.cloud.gateway.handler.FilteringWebHandler$DefaultGatewayFilterChain.filter(FilteringWebHandler.java:113)
    *___________________________________Mono.then ⇢ at org.springframework.web.reactive.result.SimpleHandlerAdapter.handle(SimpleHandlerAdapter.java:46)
    |_                         Mono.onErrorResume ⇢ at org.springframework.web.reactive.DispatcherHandler.handleResultMono(DispatcherHandler.java:168)
    |_                               Mono.flatMap ⇢ at org.springframework.web.reactive.DispatcherHandler.handleResultMono(DispatcherHandler.java:172)
    *__________________________________Mono.error ⇢ at org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerAdapter.handleException(RequestMappingHandlerAdapter.java:321)
    *________________________________Mono.flatMap ⇢ at org.springframework.web.reactive.DispatcherHandler.handle(DispatcherHandler.java:154)
    *__________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:106)
    |_                                 checkpoint ⇢ com.c4_soft.springaddons.security.oidc.starter.reactive.client.ReactiveSpringAddonsOidcClientBeans$$Lambda/0x00000238cd5fc670 [DefaultWebFilterChain]
    *__________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:106)
    |_                                 checkpoint ⇢ org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter [DefaultWebFilterChain]
    *__________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:106)
    *__________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:106)
    *__________________________Mono.switchIfEmpty ⇢ at org.springframework.security.web.server.authorization.AuthorizationWebFilter.filter(AuthorizationWebFilter.java:56)
    |_                                 checkpoint ⇢ org.springframework.security.web.server.authorization.AuthorizationWebFilter [DefaultWebFilterChain]
    *__________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:106)
    |_                         Mono.onErrorResume ⇢ at org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter.filter(ExceptionTranslationWebFilter.java:53)
    |_                                 checkpoint ⇢ org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter [DefaultWebFilterChain]
    *__________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:106)
    *___________________________________Mono.then ⇢ at org.springframework.security.web.server.authentication.logout.LogoutWebFilter.filter(LogoutWebFilter.java:63)
    *__________________________Mono.switchIfEmpty ⇢ at org.springframework.security.web.server.authentication.logout.LogoutWebFilter.filter(LogoutWebFilter.java:63)
    |_                                   Mono.map ⇢ at org.springframework.security.web.server.authentication.logout.LogoutWebFilter.filter(LogoutWebFilter.java:64)
    |_                               Mono.flatMap ⇢ at org.springframework.security.web.server.authentication.logout.LogoutWebFilter.filter(LogoutWebFilter.java:65)
    |_                               Mono.flatMap ⇢ at org.springframework.security.web.server.authentication.logout.LogoutWebFilter.filter(LogoutWebFilter.java:66)
    |_                                 checkpoint ⇢ org.springframework.security.web.server.authentication.logout.LogoutWebFilter [DefaultWebFilterChain]
    *__________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:106)
    *________________________________Mono.flatMap ⇢ at org.springframework.security.web.server.savedrequest.ServerRequestCacheWebFilter.filter(ServerRequestCacheWebFilter.java:41)
    |_                                 checkpoint ⇢ org.springframework.security.web.server.savedrequest.ServerRequestCacheWebFilter [DefaultWebFilterChain]
    *__________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:106)
    |_                                 checkpoint ⇢ org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter [DefaultWebFilterChain]
    *__________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:106)
    |_                          Mono.contextWrite ⇢ at org.springframework.security.web.server.authentication.AnonymousAuthenticationWebFilter.lambda$filter$0(AnonymousAuthenticationWebFilter.java:86)
    *___________________________________Mono.then ⇢ at org.springframework.security.web.server.authentication.AnonymousAuthenticationWebFilter.lambda$filter$0(AnonymousAuthenticationWebFilter.java:87)
    *__________________________________Mono.defer ⇢ at org.springframework.security.web.server.authentication.AnonymousAuthenticationWebFilter.filter(AnonymousAuthenticationWebFilter.java:81)
    *__________________________Mono.switchIfEmpty ⇢ at org.springframework.security.web.server.authentication.AnonymousAuthenticationWebFilter.filter(AnonymousAuthenticationWebFilter.java:81)
    |_                               Mono.flatMap ⇢ at org.springframework.security.web.server.authentication.AnonymousAuthenticationWebFilter.filter(AnonymousAuthenticationWebFilter.java:88)
    |_                                 checkpoint ⇢ org.springframework.security.web.server.authentication.AnonymousAuthenticationWebFilter [DefaultWebFilterChain]
    *__________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:106)
    *___________________________________Mono.then ⇢ at org.springframework.security.web.server.authentication.AuthenticationWebFilter.filter(AuthenticationWebFilter.java:114)
    *__________________________Mono.switchIfEmpty ⇢ at org.springframework.security.web.server.authentication.AuthenticationWebFilter.filter(AuthenticationWebFilter.java:114)
    |_                               Mono.flatMap ⇢ at org.springframework.security.web.server.authentication.AuthenticationWebFilter.filter(AuthenticationWebFilter.java:115)
    |_                         Mono.onErrorResume ⇢ at org.springframework.security.web.server.authentication.AuthenticationWebFilter.filter(AuthenticationWebFilter.java:116)
    |_                                 checkpoint ⇢ org.springframework.security.web.server.authentication.AuthenticationWebFilter [DefaultWebFilterChain]
    *__________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:106)
    |_                          Mono.contextWrite ⇢ at org.springframework.security.web.server.context.ReactorContextWebFilter.filter(ReactorContextWebFilter.java:48)
    |_                                 checkpoint ⇢ org.springframework.security.web.server.context.ReactorContextWebFilter [DefaultWebFilterChain]
    *__________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:106)
    |_                                 checkpoint ⇢ org.springframework.web.cors.reactive.CorsWebFilter [DefaultWebFilterChain]
    *__________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:106)
    |_                                 checkpoint ⇢ org.springframework.security.web.server.header.HttpHeaderWriterWebFilter [DefaultWebFilterChain]
    *__________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:106)
    |_                          Mono.contextWrite ⇢ at org.springframework.security.config.web.server.ServerHttpSecurity$ServerWebExchangeReactorContextWebFilter.filter(ServerHttpSecurity.java:3579)
    |_                                 checkpoint ⇢ org.springframework.security.config.web.server.ServerHttpSecurity$ServerWebExchangeReactorContextWebFilter [DefaultWebFilterChain]
    *__________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:106)
    *________________________________Mono.flatMap ⇢ at org.springframework.security.web.server.WebFilterChainProxy.filter(WebFilterChainProxy.java:63)
    |_                                 checkpoint ⇢ org.springframework.security.web.server.WebFilterChainProxy [DefaultWebFilterChain]
    *__________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:106)
    |_                             Mono.doOnError ⇢ at org.springframework.web.server.handler.ExceptionHandlingWebHandler.handle(ExceptionHandlingWebHandler.java:84)
    |_                         Mono.onErrorResume ⇢ at org.springframework.web.server.handler.ExceptionHandlingWebHandler.handle(ExceptionHandlingWebHandler.java:85)
    |_                             Mono.doOnError ⇢ at org.springframework.web.server.handler.ExceptionHandlingWebHandler.handle(ExceptionHandlingWebHandler.java:84)
    *__________________________________Mono.error ⇢ at org.springframework.web.server.handler.ExceptionHandlingWebHandler$CheckpointInsertingHandler.handle(ExceptionHandlingWebHandler.java:106)
    |_                                 checkpoint ⇢ HTTP GET "/ui/" [ExceptionHandlingWebHandler]
Original Stack Trace:
Caused by: java.net.ConnectException: Connection refused: getsockopt
    at java.base/sun.nio.ch.Net.pollConnect(Native Method)
    at java.base/sun.nio.ch.Net.pollConnectNow(Net.java:682)
    at java.base/sun.nio.ch.SocketChannelImpl.finishConnect(SocketChannelImpl.java:973)
    at io.netty.channel.socket.nio.NioSocketChannel.doFinishConnect(NioSocketChannel.java:337)
    at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.finishConnect(AbstractNioChannel.java:335)
    at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:776)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650)
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562)
    at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
    at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
    at java.base/java.lang.Thread.run(Thread.java:1583)
ch4mpy commented 7 months ago

You don't want a proxy in your Vue conf, the BFF already is the proxy.

ui-uri should be set with ${scheme}://localhost:1000/ui, not with ${scheme}://localhost:1000.

It is expected that you get an exception on the BFF when you try to get ${scheme}:localhost:8080/ui with your current conf: it is trying to route to ${ui-uri} which is set to ${scheme}://localhost:1000 in your BFF conf, which is wrong because the dev server is configured to listen to http://localhost:1000/ui (which is right)

TheNullablePrototype commented 7 months ago

I have removed the proxy in the Vue configuration.

Current status of request execution: image image I understand that I was making requests from localhost:1000, since localhost:8080 still does not allow a connection to /ui (see below)

Please note that there is no /ui in your example

Also added /ui to the parameter ui-uri: ui-uri: ${scheme}://localhost:1000/ui The error remained unchanged:

Whitelabel Error Page
This application has no configured error view, so you are seeing this as a fallback.

Mon Jan 22 12:00:05 MSK 2024
[72f050a6-4] There was an unexpected error (type=Internal Server Error, status=500).
Connection refused: getsockopt: localhost/127.0.0.1:1000
io.netty.channel.AbstractChannel$AnnotatedConnectException: Connection refused: getsockopt: localhost/127.0.0.1:1000
    Suppressed: The stacktrace has been enhanced by Reactor, refer to additional information below: 
Assembly trace from producer [reactor.core.publisher.MonoError] :
    reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:94)
    reactor.netty.transport.TransportConnector$MonoChannelPromise.tryFailure(TransportConnector.java:576)
Error has been observed at the following site(s):
    *__FluxOnErrorResume$ResumeSubscriber.onError ⇢ at reactor.netty.transport.TransportConnector$MonoChannelPromise.tryFailure(TransportConnector.java:576)
    *__________________________Mono.onErrorResume ⇢ at reactor.netty.transport.TransportConnector.lambda$connect$6(TransportConnector.java:166)
    *________________________________Mono.flatMap ⇢ at reactor.netty.transport.TransportConnector.connect(TransportConnector.java:165)
    *_________________________________Mono.create ⇢ at reactor.netty.resources.DefaultPooledConnectionProvider$PooledConnectionAllocator.connectChannel(DefaultPooledConnectionProvider.java:535)
    *_________________________________Mono.create ⇢ at reactor.netty.resources.PooledConnectionProvider.acquire(PooledConnectionProvider.java:126)
    *_________________________________Mono.create ⇢ at reactor.netty.http.client.HttpClientConnect$MonoHttpConnect.subscribe(HttpClientConnect.java:210)
    |_                             Mono.retryWhen ⇢ at reactor.netty.http.client.HttpClientConnect$MonoHttpConnect.subscribe(HttpClientConnect.java:275)
    *______________________________Flux.concatMap ⇢ at reactor.util.retry.RetrySpec.lambda$generateCompanion$6(RetrySpec.java:360)
    |_                           Flux.onErrorStop ⇢ at reactor.util.retry.RetrySpec.lambda$generateCompanion$6(RetrySpec.java:379)
    *________________________Flux.deferContextual ⇢ at reactor.util.retry.RetrySpec.generateCompanion(RetrySpec.java:357)
    *____________________________Mono.flatMapMany ⇢ at reactor.netty.http.client.HttpClientFinalizer.responseConnection(HttpClientFinalizer.java:102)
    *___________________________________Flux.then ⇢ at org.springframework.cloud.gateway.filter.NettyRoutingFilter.filter(NettyRoutingFilter.java:198)
    *__________________________________Mono.defer ⇢ at org.springframework.cloud.gateway.handler.FilteringWebHandler$DefaultGatewayFilterChain.filter(FilteringWebHandler.java:113)
    *__________________________________Mono.defer ⇢ at org.springframework.cloud.gateway.handler.FilteringWebHandler$DefaultGatewayFilterChain.filter(FilteringWebHandler.java:113)
    *__________________________________Mono.defer ⇢ at org.springframework.cloud.gateway.handler.FilteringWebHandler$DefaultGatewayFilterChain.filter(FilteringWebHandler.java:113)
    *__________________________________Mono.defer ⇢ at org.springframework.cloud.gateway.handler.FilteringWebHandler$DefaultGatewayFilterChain.filter(FilteringWebHandler.java:113)
    *___________________________________Mono.then ⇢ at org.springframework.cloud.gateway.filter.factory.SaveSessionGatewayFilterFactory$1.filter(SaveSessionGatewayFilterFactory.java:45)
    *__________________________________Mono.defer ⇢ at org.springframework.cloud.gateway.handler.FilteringWebHandler$DefaultGatewayFilterChain.filter(FilteringWebHandler.java:113)
    *___________________________________Mono.then ⇢ at org.springframework.cloud.gateway.filter.factory.DedupeResponseHeaderGatewayFilterFactory$1.filter(DedupeResponseHeaderGatewayFilterFactory.java:93)
    *__________________________________Mono.defer ⇢ at org.springframework.cloud.gateway.handler.FilteringWebHandler$DefaultGatewayFilterChain.filter(FilteringWebHandler.java:113)
    *__________________________________Mono.defer ⇢ at org.springframework.cloud.gateway.handler.FilteringWebHandler$DefaultGatewayFilterChain.filter(FilteringWebHandler.java:113)
    *___________________________________Mono.then ⇢ at org.springframework.cloud.gateway.filter.NettyWriteResponseFilter.filter(NettyWriteResponseFilter.java:69)
    |_                            Mono.doOnCancel ⇢ at org.springframework.cloud.gateway.filter.NettyWriteResponseFilter.filter(NettyWriteResponseFilter.java:101)
    |_                             Mono.doOnError ⇢ at org.springframework.cloud.gateway.filter.NettyWriteResponseFilter.filter(NettyWriteResponseFilter.java:102)
    *__________________________________Mono.defer ⇢ at org.springframework.cloud.gateway.handler.FilteringWebHandler$DefaultGatewayFilterChain.filter(FilteringWebHandler.java:113)
    *__________________________________Mono.defer ⇢ at org.springframework.cloud.gateway.handler.FilteringWebHandler$DefaultGatewayFilterChain.filter(FilteringWebHandler.java:113)
    |_                             Mono.doFinally ⇢ at org.springframework.cloud.gateway.filter.RemoveCachedBodyFilter.filter(RemoveCachedBodyFilter.java:35)
    *__________________________________Mono.defer ⇢ at org.springframework.cloud.gateway.handler.FilteringWebHandler$DefaultGatewayFilterChain.filter(FilteringWebHandler.java:113)
    *___________________________________Mono.then ⇢ at org.springframework.web.reactive.result.SimpleHandlerAdapter.handle(SimpleHandlerAdapter.java:46)
    |_                         Mono.onErrorResume ⇢ at org.springframework.web.reactive.DispatcherHandler.handleResultMono(DispatcherHandler.java:168)
    |_                               Mono.flatMap ⇢ at org.springframework.web.reactive.DispatcherHandler.handleResultMono(DispatcherHandler.java:172)
    *__________________________________Mono.error ⇢ at org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerAdapter.handleException(RequestMappingHandlerAdapter.java:321)
    *________________________________Mono.flatMap ⇢ at org.springframework.web.reactive.DispatcherHandler.handle(DispatcherHandler.java:154)
    *__________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:106)
    |_                                 checkpoint ⇢ com.c4_soft.springaddons.security.oidc.starter.reactive.client.ReactiveSpringAddonsOidcClientBeans$$Lambda/0x00000239815f7900 [DefaultWebFilterChain]
    *__________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:106)
    |_                                 checkpoint ⇢ org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter [DefaultWebFilterChain]
    *__________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:106)
    *__________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:106)
    *__________________________Mono.switchIfEmpty ⇢ at org.springframework.security.web.server.authorization.AuthorizationWebFilter.filter(AuthorizationWebFilter.java:56)
    |_                                 checkpoint ⇢ org.springframework.security.web.server.authorization.AuthorizationWebFilter [DefaultWebFilterChain]
    *__________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:106)
    |_                         Mono.onErrorResume ⇢ at org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter.filter(ExceptionTranslationWebFilter.java:53)
    |_                                 checkpoint ⇢ org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter [DefaultWebFilterChain]
    *__________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:106)
    *___________________________________Mono.then ⇢ at org.springframework.security.web.server.authentication.logout.LogoutWebFilter.filter(LogoutWebFilter.java:63)
    *__________________________Mono.switchIfEmpty ⇢ at org.springframework.security.web.server.authentication.logout.LogoutWebFilter.filter(LogoutWebFilter.java:63)
    |_                                   Mono.map ⇢ at org.springframework.security.web.server.authentication.logout.LogoutWebFilter.filter(LogoutWebFilter.java:64)
    |_                               Mono.flatMap ⇢ at org.springframework.security.web.server.authentication.logout.LogoutWebFilter.filter(LogoutWebFilter.java:65)
    |_                               Mono.flatMap ⇢ at org.springframework.security.web.server.authentication.logout.LogoutWebFilter.filter(LogoutWebFilter.java:66)
    |_                                 checkpoint ⇢ org.springframework.security.web.server.authentication.logout.LogoutWebFilter [DefaultWebFilterChain]
    *__________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:106)
    *________________________________Mono.flatMap ⇢ at org.springframework.security.web.server.savedrequest.ServerRequestCacheWebFilter.filter(ServerRequestCacheWebFilter.java:41)
    |_                                 checkpoint ⇢ org.springframework.security.web.server.savedrequest.ServerRequestCacheWebFilter [DefaultWebFilterChain]
    *__________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:106)
    |_                                 checkpoint ⇢ org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter [DefaultWebFilterChain]
    *__________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:106)
    |_                          Mono.contextWrite ⇢ at org.springframework.security.web.server.authentication.AnonymousAuthenticationWebFilter.lambda$filter$0(AnonymousAuthenticationWebFilter.java:86)
    *___________________________________Mono.then ⇢ at org.springframework.security.web.server.authentication.AnonymousAuthenticationWebFilter.lambda$filter$0(AnonymousAuthenticationWebFilter.java:87)
    *__________________________________Mono.defer ⇢ at org.springframework.security.web.server.authentication.AnonymousAuthenticationWebFilter.filter(AnonymousAuthenticationWebFilter.java:81)
    *__________________________Mono.switchIfEmpty ⇢ at org.springframework.security.web.server.authentication.AnonymousAuthenticationWebFilter.filter(AnonymousAuthenticationWebFilter.java:81)
    |_                               Mono.flatMap ⇢ at org.springframework.security.web.server.authentication.AnonymousAuthenticationWebFilter.filter(AnonymousAuthenticationWebFilter.java:88)
    |_                                 checkpoint ⇢ org.springframework.security.web.server.authentication.AnonymousAuthenticationWebFilter [DefaultWebFilterChain]
    *__________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:106)
    *___________________________________Mono.then ⇢ at org.springframework.security.web.server.authentication.AuthenticationWebFilter.filter(AuthenticationWebFilter.java:114)
    *__________________________Mono.switchIfEmpty ⇢ at org.springframework.security.web.server.authentication.AuthenticationWebFilter.filter(AuthenticationWebFilter.java:114)
    |_                               Mono.flatMap ⇢ at org.springframework.security.web.server.authentication.AuthenticationWebFilter.filter(AuthenticationWebFilter.java:115)
    |_                         Mono.onErrorResume ⇢ at org.springframework.security.web.server.authentication.AuthenticationWebFilter.filter(AuthenticationWebFilter.java:116)
    |_                                 checkpoint ⇢ org.springframework.security.web.server.authentication.AuthenticationWebFilter [DefaultWebFilterChain]
    *__________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:106)
    |_                          Mono.contextWrite ⇢ at org.springframework.security.web.server.context.ReactorContextWebFilter.filter(ReactorContextWebFilter.java:48)
    |_                                 checkpoint ⇢ org.springframework.security.web.server.context.ReactorContextWebFilter [DefaultWebFilterChain]
    *__________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:106)
    |_                                 checkpoint ⇢ org.springframework.web.cors.reactive.CorsWebFilter [DefaultWebFilterChain]
    *__________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:106)
    |_                                 checkpoint ⇢ org.springframework.security.web.server.header.HttpHeaderWriterWebFilter [DefaultWebFilterChain]
    *__________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:106)
    |_                          Mono.contextWrite ⇢ at org.springframework.security.config.web.server.ServerHttpSecurity$ServerWebExchangeReactorContextWebFilter.filter(ServerHttpSecurity.java:3579)
    |_                                 checkpoint ⇢ org.springframework.security.config.web.server.ServerHttpSecurity$ServerWebExchangeReactorContextWebFilter [DefaultWebFilterChain]
    *__________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:106)
    *________________________________Mono.flatMap ⇢ at org.springframework.security.web.server.WebFilterChainProxy.filter(WebFilterChainProxy.java:63)
    |_                                 checkpoint ⇢ org.springframework.security.web.server.WebFilterChainProxy [DefaultWebFilterChain]
    *__________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:106)
    |_                             Mono.doOnError ⇢ at org.springframework.web.server.handler.ExceptionHandlingWebHandler.handle(ExceptionHandlingWebHandler.java:84)
    |_                         Mono.onErrorResume ⇢ at org.springframework.web.server.handler.ExceptionHandlingWebHandler.handle(ExceptionHandlingWebHandler.java:85)
    |_                             Mono.doOnError ⇢ at org.springframework.web.server.handler.ExceptionHandlingWebHandler.handle(ExceptionHandlingWebHandler.java:84)
    *__________________________________Mono.error ⇢ at org.springframework.web.server.handler.ExceptionHandlingWebHandler$CheckpointInsertingHandler.handle(ExceptionHandlingWebHandler.java:106)
    |_                                 checkpoint ⇢ HTTP GET "/ui" [ExceptionHandlingWebHandler]
Original Stack Trace:
Caused by: java.net.ConnectException: Connection refused: getsockopt
    at java.base/sun.nio.ch.Net.pollConnect(Native Method)
    at java.base/sun.nio.ch.Net.pollConnectNow(Net.java:682)
    at java.base/sun.nio.ch.SocketChannelImpl.finishConnect(SocketChannelImpl.java:973)
    at io.netty.channel.socket.nio.NioSocketChannel.doFinishConnect(NioSocketChannel.java:337)
    at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.finishConnect(AbstractNioChannel.java:335)
    at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:776)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650)
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562)
    at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
    at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
    at java.base/java.lang.Thread.run(Thread.java:1583)
ch4mpy commented 7 months ago

Am I supposed to teach you programming or do your job (for free)? Also, should I really repeat myself?

Access to http://localhost:8080/login-options from origin http://localhost:1000 I wrote "point your browser to http://localhost:8080/ui instead of http://localhost:1000/.", but you(re still using http://localhost:1000...

Access to http://localhost:8080/api/users/me origin http://localhost:1000 same as above plus you are sending a request to /api/users/me when the prefix define for the BFF route is /bff/v1 and not /api

TheNullablePrototype commented 7 months ago

I apologize for the annoyance, the last thing I wanted to say is that I understood you perfectly, about the need to log into the browser localhost:8080/ui I wrote about it above: image I just get the error when switching to localhost:8080/ui What I wrote above about localhost:1000 is just a note that I have successfully added the base path/ui

As for the api/users/me, I completely agree, due to my carelessness, after deleting the proxy, I did not change the API links Once again, I apologize for the inconvenience.

TheNullablePrototype commented 6 months ago

Everything worked as it should, it turns out the frontend application needed to be launched --host