2

I have the following security configuration class in a Spring Cloud Gateway application. This gateway acts as an OAuth2 client handling the user authentication. After a successful authentication, I'd like to redirect to the URL of the single-page application where the user originally came from.

Example
If the user was on http://localhost:8093/profile then this should be the redirect URL.

Currently I only use a hardcoded value which works for testing purposes. Is there a way to get the "original URL" and use it as a redirection URL?

@Configuration
@EnableWebFluxSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity httpSecurity) {
        httpSecurity
                .csrf().disable()
                .authorizeExchange()
                .anyExchange().authenticated()
                .and()
                .oauth2Login()
                // Use original URL here?
                .authenticationSuccessHandler(new RedirectServerAuthenticationSuccessHandler("http://localhost:8093"))
                .and()
                .exceptionHandling().authenticationEntryPoint(new HttpStatusServerEntryPoint(HttpStatus.UNAUTHORIZED))
                .and()
                .oauth2ResourceServer().jwt();
        return httpSecurity.build();
    }
}
Robert Strauch
  • 12,055
  • 24
  • 120
  • 192
  • Don't know if you already seen these: https://www.baeldung.com/spring-security-redirect-login and this https://stackoverflow.com/questions/62980469/redirecting-user-to-original-url-after-login-page-using-onauthenticationsuccess. Not sure they work the same way with WebFlux and oauth2login. You can perform a simple test if you have access to the session object like trying to retrieve session.getAttribute("SPRING_SECURITY_SAVED_REQUEST"), as it is answered in the second link. – pringi Feb 21 '22 at 13:17

3 Answers3

2

You can try below provide the combination to Achieve what you are looking for:

First of all you need to create your Authentication Success Handler:

public class MySimpleUrlAuthenticationSuccessHandler

implements AuthenticationSuccessHandler {

protected Log logger = LogFactory.getLog(this.getClass());

private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

@Override
public void onAuthenticationSuccess(HttpServletRequest request, 
  HttpServletResponse response, Authentication authentication)
  throws IOException {

    handle(request, response, authentication);
    clearAuthenticationAttributes(request);
}

Then handle Method implementation:

protected void handle(
    HttpServletRequest request,
    HttpServletResponse response, 
    Authentication authentication
) throws IOException {
//This will provide you last URL
String targetUrl = request.getHeader("referer");

if (response.isCommitted()) {
    logger.debug(
            "Response has already been committed. Unable to redirect to "
                    + targetUrl);
    return;
}

    redirectStrategy.sendRedirect(request, response, targetUrl);
}

Just an FYI:

Note: the HTTP referer is a client-controlled value and can thus be spoofed to something entirely different or even removed. This value should not be used for any critical operation.

Jayesh Choudhary
  • 748
  • 2
  • 12
  • 30
  • Although it's been quite some time, I just stumbled over a problem: `authenticationSuccessHandler` requires a `ServerAuthenticationSuccessHandler` but it does not work with the proposed solution which is a `AuthenticationSuccessHandler`. Probably this is caused by using WebFlux? – Robert Strauch Aug 24 '22 at 11:59
2

Maybe it's too late, but I had the same problem like you. Has Jayesh said, you need to create a class "Authentication Success Handler" to add some logic and redirection after a sucessfull Oauth2 authentication. But this new class , instead of extending SimpleUrlAuthenticationSucessHandler, must extends SavedRequestAwareAuthenticationSucessHandler and override the method onAuthenticationSucess().

public class OAuth2LoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {

@Autowired
private UserService userService;

@Autowired
private MessageSource messageSource;

@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
        Authentication authentication) throws IOException, ServletException {

    CustomOAuth2User oAuth2User = (CustomOAuth2User) authentication.getPrincipal();

    User existingUser = userService.findByUsername(oAuth2User.getEmail());

    if (existingUser != null) {

        // update of user with providerId and authenticationProvider if not already done
        log.info(messageSource.getMessage("global.existing-user.oauth2-authenticated",
                new Object[] { existingUser }, LocaleContextHolder.getLocale()));

        if (existingUser.getAuthenticationProvider() == AuthProvider.LOCAL) {

            userService.updateUserFromOAuth2Authentication(oAuth2User, existingUser);

        } else if ((!Objects.equals(existingUser.getIdProvider(), oAuth2User.getproviderId())
                || existingUser.getAuthenticationProvider() != oAuth2User.getClientProvider())) {

            throw new OAuth2AuthenticationException("a problem occured with Oauth2Authentication!");
        }

    } else {

        // creation of new user
        log.info(messageSource.getMessage("global.not-existing-user.oauth2-authenticated",
                new Object[] { "createdUser" }, LocaleContextHolder.getLocale()));
        userService.saveUserFromOAuth2Authentication(oAuth2User);
    }   
    super.onAuthenticationSuccess(request, response, authentication);
}
}

In your configuration class for security, you just have to call the method successHandler()for Oauth2Login to use your new class "authentication success handler" without of course, using method defaultSucessUrl() , like this

  http.oauth2Login()
                .loginPage("/app/login")
                .userInfoEndpoint()
                .userService(oauth2UserService)
                .and()
                .successHandler(oAuth2LoginSuccessHandler)
            ;

Sorry for my bad english, i found this solution just after reading this article https://www.baeldung.com/spring-security-redirect-login

Dorian
  • 21
  • 4
0

My solution (spring boot, maven, spring data jdbc) is very similar to Dorian's, but have some differences.

// @formatter:off
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests(
                (authorize) -> authorize
                .requestMatchers("/", "/login").permitAll()
                .anyRequest().authenticated())
            .formLogin().loginPage("/login")
            .and()
            .oauth2Login(oauth2 -> oauth2
                .loginPage("/login")
                    .userInfoEndpoint()
                    .userService(oauth2UserService)
                .and()
                    .successHandler(oAuth2LoginSuccessHandler)
            );

    return http.build();
}
// @formatter:on

@Autowired
private OAuth2LoginSuccessHandler oAuth2LoginSuccessHandler;

Here is the OAuth2LoginSuccessHandler class:

@Component
public class OAuth2LoginSuccessHandler
        extends SavedRequestAwareAuthenticationSuccessHandler {
    @Autowired
    private CustomOAuth2UserService userService;

    @Autowired
    private UserRepository userRepo;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request,
            HttpServletResponse response, Authentication authentication)
            throws IOException, ServletException {

        CustomOAuth2User user = (CustomOAuth2User) authentication.getPrincipal();

        User existingUser = userRepo.findByUsername(user.getEmail());

        if (existingUser == null) {
            userService.processOAuthPostLogin(user.getEmail());
        }
        super.onAuthenticationSuccess(request, response, authentication);
    }
}
Park JongBum
  • 1,245
  • 1
  • 16
  • 27