OAuth2 Twitch

Hello! Im trying to setup OAuth2 Login using Twitch.
But the callback is not working. Error “Could not navigate to ‘login/oauth2/code/twitch’”

This is my application.properties:

# Twitch OAuth2 (ersetze die Platzhalter oder setze als ENV VAR TWITCH_CLIENT_ID/TWITCH_CLIENT_SECRET)
spring.security.oauth2.client.registration.twitch.client-id=XXX
spring.security.oauth2.client.registration.twitch.client-secret=XXX
spring.security.oauth2.client.registration.twitch.redirect-uri=http://localhost:8080/login/oauth2/code/twitch
spring.security.oauth2.client.registration.twitch.provider=twitch
spring.security.oauth2.client.registration.twitch.client-name=twitch
spring.security.oauth2.client.registration.twitch.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.twitch.scope=user:read:email

spring.security.oauth2.client.provider.twitch.authorization-uri=https://id.twitch.tv/oauth2/authorize
spring.security.oauth2.client.provider.twitch.token-uri=https://id.twitch.tv/oauth2/token
spring.security.oauth2.client.provider.twitch.user-info-uri=https://id.twitch.tv/oauth2/userinfo
spring.security.oauth2.client.provider.twitch.user-name-attribute=id

And this my SecurityConfig:

@EnableWebSecurity
@Configuration
@Import(VaadinAwareSecurityContextHolderStrategyConfiguration.class)
public class SecurityConfig {

    private static final String LOGIN_URL = "/";

    @Bean
    public SecurityFilterChain vaadinSecurityFilterChain(HttpSecurity http) throws Exception {
        http.setSharedObject(VaadinSavedRequestAwareAuthenticationSuccessHandler.class, createSuccessHandler());
        http.with(vaadin(), vaadin -> {
            vaadin.oauth2LoginPage(LOGIN_URL);
        });
        return http.build();
    }

    public VaadinSavedRequestAwareAuthenticationSuccessHandler createSuccessHandler() {
        var successHandler = new VaadinSavedRequestAwareAuthenticationSuccessHandler();
        successHandler.setDefaultTargetUrl("/dashboard");
        successHandler.setAlwaysUseDefaultTargetUrl(true);
        return successHandler;
    }
}

I hope anyone can help me and has a fix for that. THANKS!!!

I also enabled DEBUG as logging level.

Only found

2025-11-17T03:53:22.958+01:00 DEBUG 21440 --- [nio-8080-exec-8] o.s.s.web.DefaultRedirectStrategy        : Redirecting to http://localhost:8080/oauth2/authorization/twitch

Nothing for “login/oauth2/code/twitch”

According to the Vaadin online documentation, at least the oauth2 login page seems incorrect; it should be /oauth2/authorization/twitch, not /

Can you provide the login view source code, too? Might be, that your anchor needs the “router-ignore” attribute.

(and here is some sample code I have in one of my applications for Google OAuth - maybe it helps)

from the SecurityConfig

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests(auth -> auth
            .requestMatchers("/public/**").permitAll()
    );

    String oauth2LoginPage = "/login";
    http.oauth2Login(cfg -> cfg
            .loginPage(oauth2LoginPage)
            .successHandler(googleOAuth2SuccessHandler)
            .permitAll());

    http.with(VaadinSecurityConfigurer.vaadin(), configurer -> {});

    return http.build();
}

LoginView

...
Anchor anchor = new Anchor("oauth2/authorization/google?prompt=consent&access_type=offline", "Über Google anmelden");
anchor.setRouterIgnore(true);
...

Thanks! I made all changes, but this didn’t fix the issue, sadly.
My oauth2/authorization/twitch endpoint is working fine!
But for /oauth2/code/twitch its Could not navigate to 'oauth2/code/twitch'

This is my LoginView:

@Route("/")
@AnonymousAllowed
public class LoginView extends VerticalLayout {

    public LoginView() {
        Anchor login = new Anchor("oauth2/authorization/twitch", "Login with Twitch");
        login.getElement().setAttribute("theme", "primary");
        login.setRouterIgnore(true);
        add(login);
    }
}

This is my new SecurityConfig:

@Configuration
@Import(VaadinAwareSecurityContextHolderStrategyConfiguration.class)
public class SecurityConfig {

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(auth -> auth
                .requestMatchers(
                        "/public/**",
                        "/oauth2/**"
                ).permitAll()
        );

        var successHandler = new VaadinSavedRequestAwareAuthenticationSuccessHandler();
        successHandler.setDefaultTargetUrl("/dashboard");
        successHandler.setAlwaysUseDefaultTargetUrl(true);

        String oauth2LoginPage = "/oauth2/authorization/twitch";
        http.oauth2Login(cfg -> cfg
                .loginPage(oauth2LoginPage)
                .successHandler(successHandler)
                .permitAll());

        http.with(VaadinSecurityConfigurer.vaadin(), configurer -> {
            configurer.oauth2LoginPage(
                    "/oauth2/authorization/twitch",
                    "{baseUrl}/session-ended"
            );
        });

        return http.build();
    }
}

OK, now I see you have a custom login page at the / path, so my suggestion of setting /oauth2/authorization/twitch does not make sense with your setup.

Related to Marco’s observation, is there a reason your login view is at the root (@Route("/"))? Typically you’d have your default view like the Dashboard at the root ("") and the login view at "login".

And also the navigation error message is different between the first and the last post
It was Could not navigate to ‘login/oauth2/code/twitch but now it is Could not navigate to 'oauth2/code/twitch'.
Is this only a typo or did you change something else in the configuration?

For the redirect URI I would also suggest using {baseUrl}/login/oauth2/code/twitch

I’ve been experimenting a bit, yes. That’s why there are differences in the error.
So maybe my approach is wrong, but I’ll try to explain my plan, maybe it’s just that it can’t work at all.
Basically, I want the main page (/) to be a general website with a login button above it.
After logging in, you should be taken to the dashboard (/dashboard).

This would be my main page / “login” page now:

@Route("")
@AnonymousAllowed
public class LoginView extends VerticalLayout {

    public LoginView() {
        // Login-Link startet OAuth2-Flow bei Twitch - absolute URL, Vaadin Router ignorieren
        Anchor login = new Anchor("/oauth2/authorization/twitch", "Login with Twitch");
        login.getElement().setAttribute("theme", "primary");
        login.setRouterIgnore(true);
        add(login);
    }
}

This is my current SecurityConfig, where i tested some different methods, i first tried the the custom oauth2Login and also the default oauth2Login, but same result

@EnableWebSecurity
@Configuration
@Import(VaadinAwareSecurityContextHolderStrategyConfiguration.class)
public class SecurityConfig {

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(auth -> auth
                .requestMatchers(
                        "/public/**",
                        "/login/**",
                        "/oauth2/**"
                ).permitAll()
        );

        //var successHandler = new VaadinSavedRequestAwareAuthenticationSuccessHandler();
        //successHandler.setDefaultTargetUrl("/dashboard");
        //successHandler.setAlwaysUseDefaultTargetUrl(true);
        //
        //String oauth2LoginPage = "/oauth2/authorization/twitch";
        //String oauth2CodePage = "/login/oauth2/code/twitch";
        //http.oauth2Login(cfg -> cfg
        //        .redirectionEndpoint(redirectionEndpointConfig -> {
        //            redirectionEndpointConfig
        //                    .baseUri(oauth2CodePage);
        //        })
        //        .authorizationEndpoint(authorizationEndpointConfig -> {
        //            authorizationEndpointConfig
        //                    .baseUri(oauth2LoginPage);
        //        })
        //        .successHandler(successHandler)
        //        .permitAll());

        http.oauth2Login(Customizer.withDefaults());

        http.with(VaadinSecurityConfigurer.vaadin(), configurer -> {});

        return http.build();
    }
}

Did i miss something? I tried so many different ways but not a single one worked for me.

I also just changed the redirect-uri to “{baseUrl}/login/oauth2/code/twitch” as recommended by you

spring.security.oauth2.client.registration.twitch.redirect-uri={baseUrl}/login/oauth2/code/twitch

I would probably start from scratch adapting information from this guide How to configure Vaadin and Spring Security for OAuth2

Once the basic setup works, I would add the login view and update the security configuration