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.
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.