5

I tried to configure the authorization code flow as a client. As far the flow is working. I get a redirect to the login page. The oauth2 server gives me an auth code and I can exchange the code for an access token.

But I can't get the last step right: get back to the original resource. This is my SecurityConfig:

@Configuration
@EnableWebSecurity
@EnableOAuth2Client
public class SecureConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    OAuth2ClientContext oauth2ClientContext;

    @Value("${openId.userinfo}")
    private String userInfoUri;

    @Value("${openId.clientId}")
    private String clientId;

    @Value("${openId.clientSecret}")
    private String clientSecret;

    @Value("${openId.accessTokenUri}")
    private String accessTokenUri;

    @Value("${openId.userAuthorizationUri}")
    private String userAuthorizationUri;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
                http.csrf().disable()
                .addFilterAfter(ssoFilter(), BasicAuthenticationFilter.class);
    }

    private OAuth2ClientAuthenticationProcessingFilter ssoFilter() {
        OAuth2ClientAuthenticationProcessingFilter openIDFilter = new OAuth2ClientAuthenticationProcessingFilter("/resource/**");
        openIDFilter.setRestTemplate(restTemplate());
        UserInfoTokenServices tokenServices = new UserInfoTokenServices(userInfoUri, clientId);
        tokenServices.setRestTemplate(restTemplate());
        openIDFilter.setTokenServices(tokenServices);
        return openIDFilter;
    }

    @Bean
    @Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
    public OAuth2RestTemplate restTemplate() {
        return new OAuth2RestTemplate(protectedResourceDetails(), oauth2ClientContext);
    }

    @Bean
    public FilterRegistrationBean oauth2ClientFilterRegistration(
            OAuth2ClientContextFilter filter) {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(filter);
        registration.setOrder(-100);
        return registration;
    }

    @Bean
    public OAuth2ProtectedResourceDetails protectedResourceDetails() {
        AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails();
        details.setClientId(clientId);
        details.setClientSecret(clientSecret);
        details.setAccessTokenUri(accessTokenUri);
        details.setUserAuthorizationUri(userAuthorizationUri);
        details.setScope(Arrays.asList("read"));
        details.setUseCurrentUri(true);
        return details;
    }
}

And this is my controller:

@Controller
@RequestMapping("/resource")
public class TestController {

    @RequestMapping(value = "/test",  method = {RequestMethod.GET, RequestMethod.POST})
    @ResponseStatus(code = HttpStatus.OK)
    public void test(){
        System.out.println("hello world");
    }
}

In the last step spring redirect me to my base url: enter image description here

I found this forum post

It suggests saving the request in the RequestCache. But this post is about 6 years old, maybe spring offers a more elegant solution in the meantime?

EDIT: This are my dependencies:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-dependencies</artifactId>
        <version>1.5.2.RELEASE</version>
        <type>pom</type>
        <scope>import</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.security.oauth</groupId>
        <artifactId>spring-security-oauth2</artifactId>
    </dependency>
</dependencies>
alex.b
  • 184
  • 1
  • 2
  • 15
  • Where are you expecting spring to redirect you? Did you configure redirect uri? Can you share your complete flow from network tab of developer console? – Agam Apr 10 '18 at 04:10
  • @Agam To the original uri /resource/test. Yes its configured with `details.setUseCurrentUri(true);`. The flow is working correct. I can exchange the auth code for an access token but after the exchange spring redirect me to my baseurl. You can see it in the sreenshot, the auth server gives me the auth code "http://localhost:8080/resource/test?code=KtWfYH&state=ETsZAG" and spring redirect me to http://localhost:8080/ – alex.b Apr 10 '18 at 13:25
  • Incase someone came here looking for [this](https://stackoverflow.com/a/57557022/1581226) – qwerty Aug 22 '19 at 08:49

3 Answers3

2

This is what AbstractAuthenticationProcessingFilter says which OAuth2ClientAuthenticationProcessingFilter extends.

enter image description here

Try this:

        private OAuth2ClientAuthenticationProcessingFilter ssoFilter() {
                OAuth2ClientAuthenticationProcessingFilter openIDFilter = new OAuth2ClientAuthenticationProcessingFilter("/resource/**");
                openIDFilter.setRestTemplate(restTemplate());
                UserInfoTokenServices tokenServices = new UserInfoTokenServices(userInfoUri, clientId);
                tokenServices.setRestTemplate(restTemplate());
                openIDFilter.setTokenServices(tokenServices);

openIDFilter.setAuthenticationSuccessHandler(new SavedRequestAwareAuthenticationSuccessHandler());


return openIDFilter;

 }

Update:

This is my debug log for your reference.

    - Checking match of request : '/dist/i_do not_exist.html'; against '/favicon.ico'
- Checking match of request : '/dist/i_do not_exist.html'; against '/images/**'
- Checking match of request : '/dist/i_do not_exist.html'; against '/css/**'
- Checking match of request : '/dist/i_do not_exist.html'; against '/maxsession.jsp'
- /dist/i_do not_exist.html at position 1 of 12 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
- No HttpSession currently exists
- No SecurityContext was available from the HttpSession: null. A new one will be created.
- /dist/i_do not_exist.html at position 2 of 12 in additional filter chain; firing Filter: 'ConcurrentSessionFilter'
- /dist/i_do not_exist.html at position 3 of 12 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
- /dist/i_do not_exist.html at position 4 of 12 in additional filter chain; firing Filter: 'LogoutFilter'
- /dist/i_do not_exist.html at position 5 of 12 in additional filter chain; firing Filter: 'BasicAuthenticationFilter'
- /dist/i_do not_exist.html at position 6 of 12 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
- /dist/i_do not_exist.html at position 7 of 12 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
- /dist/i_do not_exist.html at position 8 of 12 in additional filter chain; firing Filter: 'SessionManagementFilter'
- /dist/i_do not_exist.html at position 9 of 12 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
- /dist/i_do not_exist.html at position 10 of 12 in additional filter chain; firing Filter: 'OAuth2ClientContextFilter'
- /dist/i_do not_exist.html at position 11 of 12 in additional filter chain; firing Filter: 'OpenIdConnectFilter'
- /dist/i_do not_exist.html at position 12 of 12 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
- Secure object: FilterInvocation: URL: /dist/i_do not_exist.html; Attributes: [isFullyAuthenticated()]
11:43:23.719 [http-nio-8080-exec-3] DEBUG o.a.camel.spring.SpringCamelContext - onApplicationEvent: org.springframework.security.access.event.AuthenticationCredentialsNotFoundEvent[source=FilterInvocation: URL: /dist/i_do not_exist.html]
- Authentication exception occurred; redirecting to authentication entry point
org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
    at org.springframework.security.access.intercept.AbstractSecurityInterceptor.credentialsNotFound(AbstractSecurityInterceptor.java:339)
    at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:198)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:115)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:199)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.oauth2.client.filter.OAuth2ClientContextFilter.doFilter(OAuth2ClientContextFilter.java:60)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:154)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilter(BasicAuthenticationFilter.java:150)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.session.ConcurrentSessionFilter.doFilter(ConcurrentSessionFilter.java:125)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:94)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:616)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:620)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:502)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1132)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:684)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1539)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1495)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)
- Publishing event: org.springframework.security.web.session.HttpSessionCreatedEvent[source=org.apache.catalina.session.StandardSessionFacade@cd8de57]
11:43:25.895 [http-nio-8080-exec-3] DEBUG o.a.camel.spring.SpringCamelContext - onApplicationEvent: org.springframework.security.web.session.HttpSessionCreatedEvent[source=org.apache.catalina.session.StandardSessionFacade@cd8de57]
- DefaultSavedRequest added to Session: DefaultSavedRequest[http://ecuio197m0221:8080/Pagos/dist/i_do%20not_exist.html]
- Calling Authentication entry point.
- Redirecting to 'http://<server>:<port>/<AppContext>/idp-login;jsessionid=CB7BAACDAEDC3A0E37AD5F75C0E38C26'
Agam
  • 1,015
  • 2
  • 11
  • 21
  • Thanks for you answer. The 'SavedRequestAwareAuthenticationSuccessHandler' is the default successHandler. But the 'ExceptionTranslationFilter' don't get called, so the RequestCache is empty and the successHandler redirect me to root. This [this forum post](http://forum.spring.io/forum/spring-projects/security/119246-redirecting-to-original-unauthenticated-page-after-spring-security-openid-authentic) suggest to add the request manually to the requestCache. I thought maybe spring supports my desired behaviour in the meantime, but apparently not. I will try to post my solution this weekend – alex.b Apr 11 '18 at 07:11
  • Well it's great that you have a solution, but for the sake of somenone in future who could find this question related, I would say that this solution is working perfectly fine for me. If I start the flow with any URL like "http://:/applicationcontext/any_thing" with Authorization code flow, I end up in browser with same URL. Secondly, If you are using authorization code flow, spring definately throws UserRedirectException, which is handled by 'ExceptionTranslationFilter' and your current request is saved in RequestCache. I am wondering why this is not happening in your case. – Agam Apr 11 '18 at 14:24
  • It's 'UserRedirectRequiredException' not 'UserRedirectException'. – Agam Apr 11 '18 at 14:51
  • Thanks for you continues support. Yes spring throws the `UserRedirectRequiredException`. I didn't express myself clearly. The `ExceptionTranslationFilter` is getting called but after the `OAuth2ClientAuthenticationProcessingFilter`. Maybe this is my problem? I have inspected again my configuration its exactly what i have posted. Do you have the same spring version? – alex.b Apr 11 '18 at 15:15
  • Just Update the answer with my debug log. May it can trigger something. – Agam Apr 11 '18 at 16:51
  • I tried to set my ssoFilter before `ExceptionTranslationFilter`. First problem was the auth code flow didint work at all. I needed to configure `.anonymous().disable()`. After this the same behavior appear as previously, i was getting redirected to root. I started to debug the `ExceptionTranslationFilter` and noticed the request is not saved in the requestCache. Apparently the request will be only saved if the exception is of instance `AuthenticationException` or `AccessDeniedException`. But the exception `UserRedirectRequiredException` (which is getting throwed) is a RuntimeException. – alex.b Apr 12 '18 at 07:37
  • Don't provide information as image which can be text. – Kalle Richter May 05 '19 at 17:35
2

It's a workaround and I don't like it. If somebody finds a better solution i would be grateful.

For the purpose I will keep the implementation simple and dirty. First I have implemented UpdateSavedRequestFilter to save request in the requestCache:

public class UpdateSavedRequestFilter extends OncePerRequestFilter {
    private RequestCache requestCache = new HttpSessionRequestCache();

    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        String queryString = request.getQueryString();
        if(!StringUtils.contains(queryString, "code") && authentication == null) {
            requestCache.saveRequest(request, response);
        }
        filterChain.doFilter(request, response);
    }
}

It didn't work as wished, I was redirected to "/resource/test" but the auth process was triggered again. So i have implemented my own Oauth2Filter. It doesn't do much, i have mostly copy code from http://www.baeldung.com/spring-security-openid-connect .My only bit to it was an extenstion of the doFilter method with a call of requiresAuthentication which check if the user is already authenticated

public class OAuth2Filter extends AbstractAuthenticationProcessingFilter {

    public OAuth2RestOperations restTemplate;

    private UserInfoTokenServices tokenServices;

    public OAuth2Filter(String defaultFilterProcessesUrl) {
        super(defaultFilterProcessesUrl);
        setAuthenticationManager(new NoopAuthenticationManager());
        SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
        setAuthenticationSuccessHandler(successHandler);
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        if (requiresAuthentication()) {
            super.doFilter(req, res, chain);
        } else {
            chain.doFilter(req, res);
        }
    }

    @Override
    public Authentication attemptAuthentication(
            HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException{
        OAuth2AccessToken accessToken;
        try {
            accessToken = restTemplate.getAccessToken();
        } catch (OAuth2Exception e) {
            throw new BadCredentialsException("Could not obtain access token", e);
        }
        try {
            OAuth2Authentication result = tokenServices.loadAuthentication(accessToken.getValue());
            if (authenticationDetailsSource!=null) {
                request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_VALUE, accessToken.getValue());
                request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_TYPE, accessToken.getTokenType());
                result.setDetails(authenticationDetailsSource.buildDetails(request));
            }
            publish(new AuthenticationSuccessEvent(result));
            return result;
        } catch (InvalidTokenException e) {
            throw new BadCredentialsException("Could not obtain user details from token", e);
        }
    }

    private void publish(ApplicationEvent event) {
        if (eventPublisher!=null) {
            eventPublisher.publishEvent(event);
        }
    }

    private static class NoopAuthenticationManager implements AuthenticationManager {

        @Override
        public Authentication authenticate(Authentication authentication) throws AuthenticationException {
            throw new UnsupportedOperationException("No authentication should be done with this AuthenticationManager");
        }

    }

    private boolean requiresAuthentication() {
        Authentication currentUser = SecurityContextHolder.getContext()
                .getAuthentication();

        if (currentUser == null) {
            return true;
        }
        OAuth2AccessToken accessToken = restTemplate.getAccessToken();
        if (accessToken == null) {
            return true;
        }
        return accessToken.isExpired();
    }

    public void setRestTemplate(OAuth2RestOperations restTemplate) {
        this.restTemplate = restTemplate;
    }

    public void setTokenServices(UserInfoTokenServices tokenServices) {
        this.tokenServices = tokenServices;
    }
}

This is the rest of my conig classes:

@Configuration
@EnableWebSecurity
public class SecureConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private OAuth2RestTemplate restTemplate;

    @Value("${openId.userinfo}")
    private String userInfoUri;

    @Value("${openId.clientId}")
    private String clientId;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .addFilterAfter(new OAuth2ClientContextFilter(), AbstractPreAuthenticatedProcessingFilter.class)
                .addFilterAfter(openIdConnectFilter(), OAuth2ClientContextFilter.class)
                .addFilterBefore(new UpdateSavedRequestFilter(), OAuth2Filter.class);
    }

    @Bean
    public OAuth2Filter openIdConnectFilter() {
        OAuth2Filter filter = new OAuth2Filter("/resource/**");
        filter.setRestTemplate(restTemplate);
        UserInfoTokenServices tokenServices = new UserInfoTokenServices(userInfoUri, clientId);
        tokenServices.setRestTemplate(restTemplate);
        filter.setTokenServices(tokenServices);
        return filter;
    }
}

@Configuration
@EnableOAuth2Client
public class OpenIdConnectConfig {
    @Value("${openId.userinfo}")
    private String userInfoUri;

    @Value("${openId.clientId}")
    private String clientId;

    @Value("${openId.clientSecret}")
    private String clientSecret;

    @Value("${openId.accessTokenUri}")
    private String accessTokenUri;

    @Value("${openId.userAuthorizationUri}")
    private String userAuthorizationUri;


    @Bean
    public OAuth2ProtectedResourceDetails protectedResourceDetails() {
        AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails();
        details.setClientId(clientId);
        details.setClientSecret(clientSecret);
        details.setAccessTokenUri(accessTokenUri);
        details.setUserAuthorizationUri(userAuthorizationUri);
        details.setScope(Arrays.asList("read"));
        details.setUseCurrentUri(true);
        return details;
    }

    @Bean
    @Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
    public OAuth2RestTemplate restTemplate(OAuth2ClientContext clientContext) {
        OAuth2RestTemplate oAuth2RestTemplate = new OAuth2RestTemplate(protectedResourceDetails(), clientContext);
        return oAuth2RestTemplate;
    }
}
alex.b
  • 184
  • 1
  • 2
  • 15
0

I'm not entirely sure if this covers OP's original scenario, because the solution might vary depending on different authentication flows.

Consider the following scenario.

  1. Browser client enters oauth login endpoint http://host/login from Referer http://host/home-page
  2. Backend returns 302 with redirect to IAM for authentication
  3. User authenticates itself and returns with authorization code to http://host/login?code=XXX
  4. Backend verifies the token and redirects user to /

We'd like to redirect the user to original Referer instead of root path.

ExceptionTranslationFilter is usually responsible for saving requests in RequestCache after authentication exception is thrown. Later, after authentication AbstractAuthenticationProcessingFilter uses the same cache to retrieve saved request, for proper redirection.

The problem is that ExceptionTranslationFilter is not involved when we use the above authentication flow.

My idea is to extend OAuth2ClientContextFilter and use RequestCache before the redirect commences. Also I needed to force RequestCache to save our request but use Referer header as the proper redirect URL. I use request wrapper to override original request URL. Probably not the prettiest solution, however it seems to be working.

public class RequestCacheOAuth2ClientContextFilter extends OAuth2ClientContextFilter {
    private final RequestCache cache = new HttpSessionRequestCache();

    @Override
    protected void redirectUser(UserRedirectRequiredException e, HttpServletRequest request, HttpServletResponse response) throws IOException {
        HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper(request) {
            @Override
            public String getRequestURI() {
                return URI.create(getReferer()).getPath();
            }

            @Override
            public StringBuffer getRequestURL() {
                return new StringBuffer(getReferer());
            }

            private String getReferer() {
                return super.getHeader(HttpHeaders.REFERER);
            }
        };
        cache.saveRequest(wrapper, response);
        super.redirectUser(e, request, response);
    }
}

Add this bean to your OAuth configuration

    @Bean
    @Primary
    public OAuth2ClientContextFilter oAuth2ClientContextFilter() {
        return new RequestCacheOAuth2ClientContextFilter();
    }

If you use @EnableOAuth2Client you may need to use @Primary

Matiz
  • 51
  • 1
  • 5
  • This looks like a crisp solution but I am not able to add this dependency in my pom.xml. Can someone help understand why? org.springframework.security.oauth spring-security-oauth2 2.5.1.RELEASE – coretechie Apr 06 '22 at 11:42