Redirect Issue in Spring Security OAuth2 Login Flow

Hello everyone!

I am working on a dummy project with a Typescript frontend and a Spring Boot backend to try out Auth0 for the first time. My frontend is quite straightforward—it essentially contains a button that, when clicked, sends a request to my server to initiate the OAuth2 login flow. My backend has a Spring Security configuration and a single controller to handle this.

Upon clicking the button, the backend successfully redirects me to the Auth0 login page. However, after logging in, instead of being redirected to my specified defaultSuccessURL (/secured), I am being redirected to /login?error. I have checked the Spring Security logs and it seems like I’m hitting the /login/oauth2/code/auth0 endpoint with a valid authorization code, but something goes amiss right after that, leading to the /login?error redirect which contains the error page with the light red box with the text “Invalid Request” and a link, upon clicking the link again i get directed to the proper page which is at http://localhost:8080/secured.

I have also verified that my Auth0 application settings are correct and the specified redirect URIs match.

Thank in advance for anyone who can provide the slightest of help.

below is some relevant code from my server-side and logs.

application.yml

spring:
  security:
    oauth2:
      client:
        registration:
          auth0:
            client-id: ***
            client-secret: ***
            scope:
              - openid
              - profile
              - email
            redirect-uri: http://localhost:8080/login/oauth2/code/auth0
        provider:
          auth0:
            issuer-uri: https://***.auth0.com/
            authorization-uri: https://***.auth0.com/authorize

spring security config class:

@Configuration
@RequiredArgsConstructor
@EnableWebSecurity
@Slf4j
public class SecurityConfig
{
    private final CorsConfigurationSource corsConfigurationSource;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws
            Exception {
        http.csrf(AbstractHttpConfigurer::disable)
                .cors(customizer -> customizer.configurationSource(corsConfigurationSource))
                .authorizeHttpRequests(authorizer -> authorizer.requestMatchers("/secured")
                        .authenticated()
                        .requestMatchers("/oauth2/authorize-client", "/error/**")
                        .permitAll()
                        .anyRequest()
                        .authenticated())
                .oauth2Login(oauth2Login -> oauth2Login.authorizationEndpoint(authorizationEndpoint -> authorizationEndpoint.baseUri(
                                "/oauth2/authorize-client"))
                        .tokenEndpoint(tokenEndpoint -> tokenEndpoint.accessTokenResponseClient(
                                accessTokenResponseClient()))
                        .defaultSuccessUrl("/secured", true)
                        .failureUrl("/login?error"));
        return http.build();
    }

    @Bean
    public OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient() {
        return new DefaultAuthorizationCodeTokenResponseClient();
    }

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplateBuilder().build();
    }

}

and my only controller :

@RestController
@RequiredArgsConstructor
public class HomeController
{

    @Value("${spring.security.oauth2.client.registration.auth0.client-id}")
    private String clientId;

    @Value("${spring.security.oauth2.client.registration.auth0.redirect-uri}")
    private String redirectUri;

    @Value("${spring.security.oauth2.client.provider.auth0.authorization-uri}")
    private String authorizationUri;


    @GetMapping("/oauth2/authorize-client")
    public ResponseEntity<Map<String, String>> login() {
        String url = UriComponentsBuilder.fromUriString(authorizationUri)
                .queryParam("response_type", "code")
                .queryParam("client_id", clientId)
                .queryParam("redirect_uri", redirectUri)
                .queryParam("scope", "openid profile email")
                .build()
                .toUriString();

        Map<String, String> response = Collections.singletonMap("redirectUrl", url);
        return ResponseEntity.ok(response);
    }

    @GetMapping("/secured")
    public Map<String, Object> getSecuredData(@AuthenticationPrincipal OidcUser principal) {
        Map<String, Object> securedData = new HashMap<>();
        if (principal != null) {
            securedData.put("user", principal.getName());
            securedData.put("claims", principal.getClaims());
        } else {
            securedData.put("error", "User not authenticated");
        }
        return securedData;
    }

}

server-side logs that occur when i hit the login button

-Securing GET /oauth2/authorize-client
-Mapped to com.projectpulse.projectpulsebe.features.user.controllers.HomeController#login()
-Secured GET /oauth2/authorize-client
-GET "/oauth2/authorize-client", parameters={}
-Mapped to com.projectpulse.projectpulsebe.features.user.controllers.HomeController#login()
-Writing [{redirectUrl=https://***.auth0.com/authorize?response_type=code&client_id=...]
-Completed 200 OK
-Set SecurityContextHolder to anonymous SecurityContext
-Securing GET /login/oauth2/code/auth0?code=***
-Redirecting to /login?error

Application Details in auth0 website:

  • Allowed Callback URLs: http://localhost:8080/login/oauth2/code/auth0
  • Allowed Logout URLs: http://localhost:8080/logout, http://localhost:3000
  • Allowed Web Origins: http://localhost:3000
  • Allowed Origins (CORS): http://localhost:3000

You can use the Okta Spring Boot starter to simplify your configuration a bit. It expects you to create an Auth0 tenant with a redirect URI that ends in okta instead of auth0.

Here’s an example with the Auth0 CLI:

auth0 apps create \
  --name "Spring Boot" \
  --description "Spring Boot Example" \
  --type regular \
  --callbacks http://localhost:8080/login/oauth2/code/okta \
  --logout-urls http://localhost:8080 \
  --reveal-secrets

Then, configure things as follows in your application.yaml:

okta:
  oauth2:
    issuer: https://<your-auth0-domain>/
    client-id: <client-id>
    client-secret: <client-secret>

You don’t need any security configuration for this to work since it already has some defaults specified. Your controller can be simplified to the following:

@RestController
@RequiredArgsConstructor
public class HomeController {

    @GetMapping("/secured")
    public Map<String, Object> getSecuredData(@AuthenticationPrincipal OidcUser principal) {
        Map<String, Object> securedData = new HashMap<>();
        if (principal != null) {
            securedData.put("user", principal.getName());
            securedData.put("claims", principal.getClaims());
        } else {
            securedData.put("error", "User not authenticated");
        }
        return securedData;
    }

}

Then, start your app and access http://localhost:8080/secured. You’ll be redirected to log in, then back to your app and its /secured endpoint.

We also have an Authentication in Spring Boot lab you might find useful.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.