springspring-bootspring-socialspring-social-facebook

Spring social The OAuth2 'state' parameter is missing or doesn't match


I'm developing a spring boot application and I need to use spring social login for facebook. I tried different strategies and tutorials to achieve that, but every time after user is redirected from facebook to my application, I get the error saying 'state' parameter doesn't exist or not match. I know this parameter is something like CSRF protection and I can see that spring is sending it when user is redirecting to facebook. But I almost get lost inside facebook response. I can see the parameter exists in the set-cookie header from facebook, but not as a parameter.

I can see more people have faced this issue before, but none of the answers worked for me and they were quite old questions. There is also an open issue on github about this. Is it a bug? or cant it be fixed through some configuration?

I'm using spring security and spring session redis in this project too. While I have tried many different configuration, here is the last one I have been working on:

SOCIAL CONFIG

@Configuration
@EnableSocial
public class SocialConfig
        implements SocialConfigurer
{


    private final Environment environment;
    private final MongoOperations mongoOperations;
    private final FacebookConnectionSignup facebookConnectionSignup;

    @Autowired
    public SocialConfig(Environment environment, MongoOperations mongoOperations, FacebookConnectionSignup facebookConnectionSignup) {
        this.environment = environment;
        this.mongoOperations = mongoOperations;
        this.facebookConnectionSignup = facebookConnectionSignup;
    }

    @Bean
    public TextEncryptor textEncryptor(){
        return noOpText();
    }


    @Override
    public void addConnectionFactories(ConnectionFactoryConfigurer connectionFactoryConfigurer, Environment environment) {
        connectionFactoryConfigurer.addConnectionFactory(new FacebookConnectionFactory(
                environment.getProperty("spring.social.facebook.appId"),
                environment.getProperty("spring.social.facebook.appSecret")));
    }

    @Override
    public UserIdSource getUserIdSource() {
        return new AuthenticationNameUserIdSource();
    }

    @Override
    public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
        MongoUsersConnectionRepository mongoUsersConnectionRepository = new MongoUsersConnectionRepository(
                mongoOperations,
                connectionFactoryLocator,
                new MongoConnectionTransformers(
                    connectionFactoryLocator,
                    textEncryptor()
                )
        );
        mongoUsersConnectionRepository.setConnectionSignUp(facebookConnectionSignup);
        return mongoUsersConnectionRepository;
    }

    @Bean
    public SocialUserDetailsService socialUserDetailsService(UserDetailsService customUserDetailsService){
        return new SimpleSocialUserDetailsService(customUserDetailsService);
    }

}

SECURITY CONFIG

@Configuration
@EnableGlobalMethodSecurity()
@EnableRedisHttpSession
@Order(-10)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .httpBasic().disable()
                .csrf().disable()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
//                .sessionFixation().changeSessionId()
                .and()
                .authorizeRequests()
                    .antMatchers("/").permitAll()
                    .antMatchers("/admin/**").authenticated()
                    .antMatchers("/m/**").authenticated()
                .and()
                .headers().frameOptions().disable()
                .and()
                .requestCache()
                .requestCache(new NullRequestCache())
                .and()
                .cors()
                .and()
                .rememberMe()
                .and().logout().clearAuthentication(true).logoutSuccessHandler(customLogoutSuccessHandler)
                .and().apply(new SpringSocialConfigurer()
                                    .postLoginUrl("/")
                                    .defaultFailureUrl("/#/login")
                                    .alwaysUsePostLoginUrl(true));
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public static AuthenticationTrustResolver getAuthenticationTrustResolver() {
        return new AuthenticationTrustResolverImpl();
    }

    @Bean("authenticationManager")
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }


    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(customUserDetailsService);
    }

    @Bean
    public CookieSerializer cookieSerializer() {
        DefaultCookieSerializer serializer = new DefaultCookieSerializer();
        serializer.setCookieName(Setting.COOKIE_NAME);
        serializer.setCookiePath("/");
        serializer.setUseSecureCookie(false);
        serializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$");
        return serializer;
    }

    @Bean
    public ProviderSignInController providerSignInController() throws Exception {
        ((MongoUsersConnectionRepository) usersConnectionRepository)
                .setConnectionSignUp(facebookConnectionSignup);

        ProviderSignInController providerSignInController = new ProviderSignInController(
                connectionFactoryLocator, usersConnectionRepository, facebookSignInAdapter);
        List<ProviderSignInInterceptor<?>> list = new ArrayList<>();
        list.add(new FacebookConnectionInterceptor());
        providerSignInController.setSessionStrategy(new HttpSessionSessionStrategy());
        providerSignInController.setSignInInterceptors(list);
        providerSignInController.afterPropertiesSet();
        return providerSignInController;
    }


}

SIGNIN ADAPTER

@Component
public class FacebookSignInAdapter implements SignInAdapter {
    private UserService userService;
    private AuthenticationService authenticationService;

    @Autowired
    public FacebookSignInAdapter(UserService userService, AuthenticationService authenticationService) {
        this.userService = userService;
        this.authenticationService = authenticationService;
    }

    @Override
    public String signIn(
            String localUserId,
            Connection<?> connection,
            NativeWebRequest request) {

        final String email = connection.getDisplayName();

        final UserEntity userEntity = userService.getUserEntityFromUnknownIdentity(email);
        authenticationService.authenticateWithoutPassword(userEntity.getUsername());

        return email;
    }
}

I'm running this on localhost:8080. The exception:

2018-09-01 09:48:16.054 ERROR 7169 --- [nio-8080-exec-6] o.s.s.c.web.ProviderSignInController     : Exception while completing OAuth 2 connection: 

java.lang.IllegalStateException: The OAuth2 'state' parameter is missing or doesn't match.
    at org.springframework.social.connect.web.ConnectSupport.verifyStateParameter(ConnectSupport.java:173) ~[spring-social-web-1.1.6.RELEASE.jar:1.1.6.RELEASE]
    at org.springframework.social.connect.web.ConnectSupport.completeConnection(ConnectSupport.java:155) ~[spring-social-web-1.1.6.RELEASE.jar:1.1.6.RELEASE]
    at org.springframework.social.connect.web.ProviderSignInController.oauth2Callback(ProviderSignInController.java:228) ~[spring-social-web-1.1.6.RELEASE.jar:1.1.6.RELEASE]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_181]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_181]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_181]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_181]
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:209) [spring-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136) [spring-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102) [spring-webmvc-5.0.8.RELEASE.jar:5.0.8.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:877) [spring-webmvc-5.0.8.RELEASE.jar:5.0.8.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:783) [spring-webmvc-5.0.8.RELEASE.jar:5.0.8.RELEASE]
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) [spring-webmvc-5.0.8.RELEASE.jar:5.0.8.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991) [spring-webmvc-5.0.8.RELEASE.jar:5.0.8.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925) [spring-webmvc-5.0.8.RELEASE.jar:5.0.8.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974) [spring-webmvc-5.0.8.RELEASE.jar:5.0.8.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:866) [spring-webmvc-5.0.8.RELEASE.jar:5.0.8.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:635) [tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851) [spring-webmvc-5.0.8.RELEASE.jar:5.0.8.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) [tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) [tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) [tomcat-embed-websocket-8.5.32.jar:8.5.32]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.springframework.boot.actuate.web.trace.servlet.HttpTraceFilter.doFilterInternal(HttpTraceFilter.java:90) [spring-boot-actuator-2.0.4.RELEASE.jar:2.0.4.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:320) [spring-security-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127) [spring-security-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91) [spring-security-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) [spring-security-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137) [spring-security-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111) [spring-security-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:150) [spring-security-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170) [spring-security-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) [spring-security-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200) [spring-security-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) [spring-security-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:96) [spring-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:66) [spring-security-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) [spring-security-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) [spring-security-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215) [spring-security-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178) [spring-security-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357) [spring-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270) [spring-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
    at ...

2018-09-01 10:05:50.679  INFO 7169 --- [io-8080-exec-10] o.apache.coyote.http11.Http11Processor   : Error parsing HTTP request header
 Note: further occurrences of HTTP header parsing errors will be logged at DEBUG level.

java.lang.IllegalArgumentException: Invalid character found in method name. HTTP method names must be tokens
    at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:428) ~[tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:684) ~[tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:800) [tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1471) [tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.32.jar:8.5.32]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_181]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_181]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.32.jar:8.5.32]
    at java.lang.Thread.run(Thread.java:748) [na:1.8.0_181]

EXTRA INFO


Here is the request timeline provided by insomnia where I am testing the login process.

> POST /signin/facebook HTTP/1.1
> Host: localhost:8080
> User-Agent: insomnia/6.0.2
> Content-Type: multipart/form-data; boundary=X-INSOMNIA-BOUNDARY
> Accept: */*
> Content-Length: 112
| --X-INSOMNIA-BOUNDARY
| Content-Disposition: form-data; name="scope"
| public_profile
| --X-INSOMNIA-BOUNDARY--


< HTTP/1.1 302 
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< Set-Cookie: JSESSIONID=MzA4YjJiZGMtNmU1Yy00ODQ0LWFiNDYtYjkyZjg5NjVjYTc4; Path=/; HttpOnly
< Location: https://www.facebook.com/v1.0/dialog/oauth?client_id=*****************&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fsignin%2Ffacebook&scope=public_profile&state=d8a641c9-8748-4a95-b603-737c956a99a9
< Content-Language: en-US
< Content-Length: 0
< Date: Sat, 01 Sep 2018 05:18:07 GMT


> POST /v1.0/dialog/oauth?client_id=*****************&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fsignin%2Ffacebook&scope=public_profile&state=d8a641c9-8748-4a95-b603-737c956a99a9 HTTP/1.1
> Host: www.facebook.com
> User-Agent: insomnia/6.0.2
> Content-Type: multipart/form-data; boundary=X-INSOMNIA-BOUNDARY
> Accept: */*
> Content-Length: 112
| --X-INSOMNIA-BOUNDARY
| Content-Disposition: form-data; name="scope"
| public_profile
| --X-INSOMNIA-BOUNDARY--


< HTTP/1.1 302 Found
< X-XSS-Protection: 0
< Pragma: no-cache
< Location: https://m.facebook.com/v3.1/dialog/oauth?redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fsignin%2Ffacebook&state=d8a641c9-8748-4a95-b603-737c956a99a9&scope=public_profile&response_type=code&client_id=*****************
< Cache-Control: private, no-cache, no-store, must-revalidate
< X-Frame-Options: DENY
< Strict-Transport-Security: max-age=15552000; preload
< X-Content-Type-Options: nosniff
< Expires: Sat, 01 Jan 2000 00:00:00 GMT
< facebook-api-version: v3.1
< Content-Type: text/html; charset="utf-8"
< X-FB-Debug: IzMwWhuz/h6oL3pLb7+7KWm0ll5EAPKo3e2aJjVwLkGcGvkIKH2tDaWQM0P3LSSygnoxsBBkn343+xe0I+flRQ==
< Date: Sat, 01 Sep 2018 05:18:07 GMT
< Connection: keep-alive
< Content-Length: 0


> POST /v3.1/dialog/oauth?redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fsignin%2Ffacebook&state=d8a641c9-8748-4a95-b603-737c956a99a9&scope=public_profile&response_type=code&client_id=***************** HTTP/1.1
> Host: m.facebook.com
> User-Agent: insomnia/6.0.2
> Content-Type: multipart/form-data; boundary=X-INSOMNIA-BOUNDARY
> Accept: */*
> Content-Length: 112
| --X-INSOMNIA-BOUNDARY
| Content-Disposition: form-data; name="scope"
| public_profile
| --X-INSOMNIA-BOUNDARY--
* We are completely uploaded and fine
< HTTP/1.1 302 Found
< Location: https://m.facebook.com/login.php?skip_api_login=1&api_key=*****************&signed_next=1&next=https%3A%2F%2Fm.facebook.com%2Fv3.1%2Fdialog%2Foauth%3Fredirect_uri%3Dhttp%253A%252F%252Flocalhost%253A8080%252Fsignin%252Ffacebook%26state%3Dd8a641c9-8748-4a95-b603-737c956a99a9%26scope%3Dpublic_profile%26response_type%3Dcode%26client_id%3D*****************%26ret%3Dlogin%26logger_id%3Dfb742942-3c31-c425-5749-882c6c0ef46c&cancel_url=http%3A%2F%2Flocalhost%3A8080%2Fsignin%2Ffacebook%3Ferror%3Daccess_denied%26error_code%3D200%26error_description%3DPermissions%2Berror%26error_reason%3Duser_denied%26state%3Dd8a641c9-8748-4a95-b603-737c956a99a9%23_%3D_&display=touch&locale=en_GB&logger_id=fb742942-3c31-c425-5749-882c6c0ef46c&_rdr
< X-Frame-Options: DENY
< X-XSS-Protection: 0
< Cache-Control: private, no-cache, no-store, must-revalidate
< Expires: Sat, 01 Jan 2000 00:00:00 GMT
< Access-Control-Allow-Origin: https://m.facebook.com
< Access-Control-Expose-Headers: X-FB-Debug, X-Loader-Length
< Pragma: no-cache
< Vary: Origin
< Access-Control-Allow-Credentials: true
< Access-Control-Allow-Methods: OPTIONS
< Strict-Transport-Security: max-age=15552000; preload; includeSubDomains
< Content-Type: text/html; charset=utf-8
< X-Content-Type-Options: nosniff
< facebook-api-version: v3.1
< Set-Cookie: datr=DyGKW_ZKdOwz5HKz9nMlERCC; expires=Mon, 31-Aug-2020 05:18:07 GMT; Max-Age=63072000; path=/; domain=.facebook.com; secure; httponly
< Set-Cookie: reg_fb_ref=https%3A%2F%2Fm.facebook.com%2Fv3.1%2Fdialog%2Foauth%3Fredirect_uri%3Dhttp%253A%252F%252Flocalhost%253A8080%252Fsignin%252Ffacebook%26state%3Dd8a641c9-8748-4a95-b603-737c956a99a9%26scope%3Dpublic_profile%26response_type%3Dcode%26client_id%3D*****************; path=/; domain=.facebook.com; secure; httponly
< Set-Cookie: reg_fb_gate=https%3A%2F%2Fm.facebook.com%2Fv3.1%2Fdialog%2Foauth%3Fredirect_uri%3Dhttp%253A%252F%252Flocalhost%253A8080%252Fsignin%252Ffacebook%26state%3Dd8a641c9-8748-4a95-b603-737c956a99a9%26scope%3Dpublic_profile%26response_type%3Dcode%26client_id%3D*****************; path=/; domain=.facebook.com; secure; httponly
< X-FB-Debug: mTfLJKXvLxLJkSzdZfSkNjnljOEAKuIf+Wn7qnRU85Cd0FhFNebW5D6zCharUAZdfK89GRl886DDq3l2q1m6RQ==
< Date: Sat, 01 Sep 2018 05:18:07 GMT
< Connection: keep-alive
< Content-Length: 0

> POST /login.php?skip_api_login=1&api_key=*****************&signed_next=1&next=https%3A%2F%2Fm.facebook.com%2Fv3.1%2Fdialog%2Foauth%3Fredirect_uri%3Dhttp%253A%252F%252Flocalhost%253A8080%252Fsignin%252Ffacebook%26state%3Dd8a641c9-8748-4a95-b603-737c956a99a9%26scope%3Dpublic_profile%26response_type%3Dcode%26client_id%3D*****************%26ret%3Dlogin%26logger_id%3Dfb742942-3c31-c425-5749-882c6c0ef46c&cancel_url=http%3A%2F%2Flocalhost%3A8080%2Fsignin%2Ffacebook%3Ferror%3Daccess_denied%26error_code%3D200%26error_description%3DPermissions%2Berror%26error_reason%3Duser_denied%26state%3Dd8a641c9-8748-4a95-b603-737c956a99a9%23_%3D_&display=touch&locale=en_GB&logger_id=fb742942-3c31-c425-5749-882c6c0ef46c&_rdr HTTP/1.1
> Host: m.facebook.com
> User-Agent: insomnia/6.0.2
> Cookie: datr=DyGKW_ZKdOwz5HKz9nMlERCC; reg_fb_gate=https%3A%2F%2Fm.facebook.com%2Fv3.1%2Fdialog%2Foauth%3Fredirect_uri%3Dhttp%253A%252F%252Flocalhost%253A8080%252Fsignin%252Ffacebook%26state%3Dd8a641c9-8748-4a95-b603-737c956a99a9%26scope%3Dpublic_profile%26response_type%3Dcode%26client_id%3D*****************; reg_fb_ref=https%3A%2F%2Fm.facebook.com%2Fv3.1%2Fdialog%2Foauth%3Fredirect_uri%3Dhttp%253A%252F%252Flocalhost%253A8080%252Fsignin%252Ffacebook%26state%3Dd8a641c9-8748-4a95-b603-737c956a99a9%26scope%3Dpublic_profile%26response_type%3Dcode%26client_id%3D*****************
> Content-Type: multipart/form-data; boundary=X-INSOMNIA-BOUNDARY
> Accept: */*
> Content-Length: 112
| --X-INSOMNIA-BOUNDARY
| Content-Disposition: form-data; name="scope"
| public_profile
| --X-INSOMNIA-BOUNDARY--

< HTTP/1.1 200 OK
< X-Frame-Options: DENY
< X-XSS-Protection: 0
< Cache-Control: private, no-cache, no-store, must-revalidate
< Access-Control-Allow-Origin: https://m.facebook.com
< Access-Control-Expose-Headers: X-FB-Debug, X-Loader-Length
< Pragma: no-cache
< Vary: Origin
< Content-Type: text/html; charset=utf-8
< X-Content-Type-Options: nosniff
< Access-Control-Allow-Credentials: true
< Access-Control-Allow-Methods: OPTIONS
< Strict-Transport-Security: max-age=15552000; preload; includeSubDomains
< Expires: Sat, 01 Jan 2000 00:00:00 GMT
< Set-Cookie: datr=DyGKW_ZKdOwz5HKz9nMlERCC; expires=Mon, 31-Aug-2020 05:18:08 GMT; Max-Age=63072000; path=/; domain=.facebook.com; secure; httponly
< Set-Cookie: reg_fb_ref=https%3A%2F%2Fm.facebook.com%2Flogin.php%3Fskip_api_login%3D1%26api_key%3D*****************%26signed_next%3D1%26next%3Dhttps%253A%252F%252Fm.facebook.com%252Fv3.1%252Fdialog%252Foauth%253Fredirect_uri%253Dhttp%25253A%25252F%25252Flocalhost%25253A8080%25252Fsignin%25252Ffacebook%2526state%253Dd8a641c9-8748-4a95-b603-737c956a99a9%2526scope%253Dpublic_profile%2526response_type%253Dcode%2526client_id%253D*****************%2526ret%253Dlogin%2526logger_id%253Dfb742942-3c31-c425-5749-882c6c0ef46c%26cancel_url%3Dhttp%253A%252F%252Flocalhost%253A8080%252Fsignin%252Ffacebook%253Ferror%253Daccess_denied%2526error_code%253D200%2526error_description%253DPermissions%252Berror%2526error_reason%253Duser_denied%2526state%253Dd8a641c9-8748-4a95-b603-737c956a99a9%2523_%253D_%26display%3Dtouch%26locale%3Den_GB%26logger_id%3Dfb742942-3c31-c425-5749-882c6c0ef46c; path=/; domain=.facebook.com; secure; httponly
< Set-Cookie: sb=ECGKW6bOTMEfm2xndqZoMTNO; expires=Mon, 31-Aug-2020 05:18:08 GMT; Max-Age=63072000; path=/; domain=.facebook.com; secure; httponly
< Vary: Accept-Encoding
< X-FB-Debug: OnwhdsOlxvieobB7fQUJzvrM0ZsEsBT0GmQcjwly4PrW9W+LqTolqmH1tic3u2pfnvLrOHcG89yVOutAWdihVQ==
< Date: Sat, 01 Sep 2018 05:18:08 GMT
< Transfer-Encoding: chunked
< Connection: keep-alive

Solution

  • After around three days today I finally found the solution.

    First of all, The problem I was facing was because of this exception:

    2018-09-01 10:05:50.679  INFO 7169 --- [io-8080-exec-10] o.apache.coyote.http11.Http11Processor   : Error parsing HTTP request header
     Note: further occurrences of HTTP header parsing errors will be logged at DEBUG level.
    
    java.lang.IllegalArgumentException: Invalid character found in method name. HTTP method names must be tokens
        at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:428) ~[tomcat-embed-core-8.5.32.jar:8.5.32]
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:684) ~[tomcat-embed-core-8.5.32.jar:8.5.32]
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.32.jar:8.5.32]
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:800) [tomcat-embed-core-8.5.32.jar:8.5.32]
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1471) [tomcat-embed-core-8.5.32.jar:8.5.32]
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.32.jar:8.5.32]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_181]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_181]
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.32.jar:8.5.32]
        at java.lang.Thread.run(Thread.java:748) [na:1.8.0_181]
    

    with a little search I found out facebook has began forcing HTTPS in callback urls.

    After that, I had to fix call back url. The callback url that is set in application must match valid ones in facebook setting.

    So I made this change:

    @Bean
        public ProviderSignInController providerSignInController(UsersConnectionRepository usersConnectionRepository, ConnectionFactoryLocator connectionFactoryLocator, FacebookSignInAdapter facebookSignInAdapter) throws Exception {
            ((MongoUsersConnectionRepository) usersConnectionRepository)
                    .setConnectionSignUp(facebookConnectionSignup);
    
            ProviderSignInController providerSignInController = new ProviderSignInController(
                    connectionFactoryLocator, usersConnectionRepository, facebookSignInAdapter);
            List<ProviderSignInInterceptor<?>> list = new ArrayList<>();
            list.add(new FacebookConnectionInterceptor());
            providerSignInController.setSessionStrategy(new HttpSessionSessionStrategy());
            providerSignInController.setSignInInterceptors(list);
    
        // **IMPORTANT PART** >>
        providerSignInController.setApplicationUrl("https://test.dropinmessenger.com");
            providerSignInController.afterPropertiesSet();
            return providerSignInController;
        }
    

    after fixing the url (I could not use https on localhost, not sure even if this works on localhost), I had to provide valid callback_uri in facebook too:

    Valid OAuth redirect url

    The next error I faced was because I was not providing secret_proof or something close to that. The quickest fix was to turn it off from App Setting > Advanced > Require App Secret which does not seem to be safest solution.

    ATTENTION Also make sure you are using latest spring-social-facebook dependency. these didn't work in version 1.1.1RELEASE, I used 2.0.3RELEASE.