Search code examples
javaspringlog4jspring-socialspring-social-facebook

Enabling logging for Spring Social


I have a Spring MVC application which uses Spring Social Facebook to enable Facebook login. This is working fine on my local machine, but it currently doesn't work in my staging environment. My problem is that I cannot figure out what goes wrong. When I try to login, I am redirected to Facebook to authorize the application. This part works fine. But when I give authorize the app, all that happens is that I am redirected back to http://example.com/signin?error=provider#_=_, which gives a 404 Not Found error.

This is not terrible useful for debugging the source of the error, and looking through my Tomcat log files, I cannot find any information that could help me find the error. No stack traces, error messages or the like. I have enabled log4j in my project, but I do not see any logs from Spring Social. Actually I am not sure how to configure logging for Spring Social, but I tried the following in my log4j2.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="DEBUG">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </Console>
    </Appenders>

    <Loggers>
        <Logger name="com.example" level="trace">
            <AppenderRef ref="Console" />
        </Logger>

        <Logger name="org.springframework.social" level="all">
            <AppenderRef ref="Console" />
        </Logger>

        <Logger name="org.springframework.social.facebook" level="all">
            <AppenderRef ref="Console" />
        </Logger>

        <Logger name="org.springframework.social.security" level="all">
            <AppenderRef ref="Console" />
        </Logger>

        <Root level="error">
            <AppenderRef ref="Console" />
        </Root>
    </Loggers>
</Configuration>

However, I do not see any Spring Social logs in my console. Logging works fine if I write to it in my application like below, so logging should be working OK.

@Controller
public class IndexController {
    private static Logger logger = LogManager.getLogger(IndexController.class);

    @RequestMapping("/")
    public String home() {
        logger.debug("Hello there from logger!");
        return "home";
    }
}

How do I configure logging for Spring Social? I found a few examples that configure log4j the way I do (e.g. one used for Spring Social tests), but otherwise nothing. I suppose the library supports this, because otherwise I don't know how to debug errors. Or is there any other way that I can find out what is triggering the error?

Below is my Spring Social configuration, even though I don't think it matters in this case.

@Configuration
public class SocialConfig {
    @Autowired
    private DataSource dataSource;

    @Autowired
    private TextEncryptor textEncryptor;

    @Autowired
    private AccountService accountService;

    @Autowired
    private AccountRepository accountRepository;

    @Value("${facebook.app.id}")
    private String facebookAppId;

    @Value("${facebook.app.secret}")
    private String facebookAppSecret;

    @Bean
    public ProviderSignInController providerSignInController() {
        ProviderSignInController controller = new ProviderSignInController(this.connectionFactoryLocator(), this.usersConnectionRepository(), new SpringSecuritySignInAdapter(this.accountRepository));
        controller.addSignInInterceptor(new RedirectToPreviousPageInterceptor(controller));

        return controller;
    }

    @Bean
    public ConnectionFactoryRegistry connectionFactoryLocator() {
        ConnectionFactoryRegistry connectionFactoryRegistry = new ConnectionFactoryRegistry();
        List<ConnectionFactory<?>> connectionFactories = new ArrayList<ConnectionFactory<?>>();
        connectionFactories.add(this.facebookConnectionFactory());
        connectionFactoryRegistry.setConnectionFactories(connectionFactories);

        return connectionFactoryRegistry;
    }

    @Bean
    public FacebookConnectionFactory facebookConnectionFactory() {
        FacebookConnectionFactory connectionFactory = new FacebookConnectionFactory(this.facebookAppId, this.facebookAppSecret);
        connectionFactory.setScope("email");

        return connectionFactory;
    }

    @Bean
    public JdbcUsersConnectionRepository usersConnectionRepository() {
        JdbcUsersConnectionRepository repository = new JdbcUsersConnectionRepository(this.dataSource, this.connectionFactoryLocator(), this.textEncryptor);
        repository.setConnectionSignUp(this.accountService);

        return repository;
    }
}

Thank you very much in advance.


Solution

  • I managed to solve this after a lot of trial and error. It turned out that my project dependencies were incorrect, and the logging by Spring Framework was not correctly sent to log4j via slf4j. I used the following Maven dependencies:

    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-api</artifactId>
        <version>2.2</version>
    </dependency>
    
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.2</version>
    </dependency>
    
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.5</version>
    </dependency>
    
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-slf4j-impl</artifactId>
        <version>2.2</version>
    </dependency>
    

    Then I enabled logging for Spring Framework with the log4j2.xml posted in the question, and buried within a billion logs, I found the following:

    ERROR org.springframework.social.connect.web.ProviderSignInController - Exception while completing OAuth 2 connection: 
    java.lang.IllegalArgumentException: Unable to initialize due to invalid secret key
        at org.springframework.security.crypto.encrypt.CipherUtils.initCipher(CipherUtils.java:110) ~[spring-security-core-3.2.5.RELEASE.jar:3.2.5.RELEASE]
        at org.springframework.security.crypto.encrypt.AesBytesEncryptor.encrypt(AesBytesEncryptor.java:65) ~[spring-security-core-3.2.5.RELEASE.jar:3.2.5.RELEASE]
        at org.springframework.security.crypto.encrypt.HexEncodingTextEncryptor.encrypt(HexEncodingTextEncryptor.java:36) ~[spring-security-core-3.2.5.RELEASE.jar:3.2.5.RELEASE]
        at org.springframework.social.connect.jdbc.JdbcConnectionRepository.encrypt(JdbcConnectionRepository.java:236) ~[spring-social-core-1.1.0.RELEASE.jar:1.1.0.RELEASE]
        at org.springframework.social.connect.jdbc.JdbcConnectionRepository.addConnection(JdbcConnectionRepository.java:167) ~[spring-social-core-1.1.0.RELEASE.jar:1.1.0.RELEASE]
        at org.springframework.social.connect.jdbc.JdbcUsersConnectionRepository.findUserIdsWithConnection(JdbcUsersConnectionRepository.java:89) ~[spring-social-core-1.1.0.RELEASE.jar:1.1.0.RELEASE]
        at org.springframework.social.connect.web.ProviderSignInController.handleSignIn(ProviderSignInController.java:260) ~[spring-social-web-1.1.0.RELEASE.jar:1.1.0.RELEASE]
        at org.springframework.social.connect.web.ProviderSignInController.oauth2Callback(ProviderSignInController.java:217) [spring-social-web-1.1.0.RELEASE.jar:1.1.0.RELEASE]
        ...
    Caused by: java.security.InvalidKeyException: Illegal key size
        at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1034) ~[?:1.8.0_20]
        at javax.crypto.Cipher.implInit(Cipher.java:800) ~[?:1.8.0_20]
        at javax.crypto.Cipher.chooseProvider(Cipher.java:859) ~[?:1.8.0_20]
        at javax.crypto.Cipher.init(Cipher.java:1370) ~[?:1.8.0_20]
        at javax.crypto.Cipher.init(Cipher.java:1301) ~[?:1.8.0_20]
        at org.springframework.security.crypto.encrypt.CipherUtils.initCipher(CipherUtils.java:105) ~[spring-security-core-3.2.5.RELEASE.jar:3.2.5.RELEASE]
        ... 85 more
    

    As you can see, it was caused by the key length being too long, because the JDK only supports 128-bit encryption by default, due to politics. To solve this, I followed the advice from this SO question. It is explained in my details in this excellent blog post. For OS X, JAVA_HOME is at /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home, so you would paste the files into /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home/jre/lib/security. This problem occurred because I was using a noOpText text encryptor on my development machine, and when I moved my application to the staging environment, I had the application set up to use a querable text encryptor. After ensuring that the key length was no longer limited to 128 bits, I had to increase the length of my userconnection table's accesstoken column to varchar(255). That's it! It now works correctly.

    I hope this helps someone in a similar situation.