Implementing a Remember me for Spring-Social
I have a spring security enabled project. I have implemented remember me using a login form which works fine. But I also have social media login accounts from google / facebook which works great. The problem is that they don't remember the user. Is there a way to set up a similar "remember me" functionality?
My current spring config for normal login page:
<http access-denied-page="/login?authorization_error=true"
disable-url-rewriting="true" authentication-manager-ref="formAuthenticationManager"
xmlns="http://www.springframework.org/schema/security">
<intercept-url pattern="/account/**" access="ROLE_USER" />
<remember-me key="iRemember"
token-validity-seconds="1209600" user-service-ref="formClientDetailsUserService" />
<form-login authentication-failure-url="/login?authentication_error=true"
default-target-url="/account/" login-page="/login"
login-processing-url="/login.do" />
<logout logout-success-url="http://example.com" logout-url="/logout" />
<anonymous />
</http>
source to share
We switched to Java configuration and created a "remember me" service:
@Bean
public MyRememberMeServices myRememberMeServices(){
MyRememberMeServices service = new MyRememberMeServices (REMEMBERME_KEY, formUserDetailsService);
service.setAlwaysRemember(true);
service.setCookieName("xxxx");
service.setParameter("_spring_security_remember_me");
service.setTokenValiditySeconds(123);
return service;
};
and then in the SignInAdapter implementation for the social login:
@Override
public String signIn(String userId, Connection<?> connection, NativeWebRequest request) {
// load the user based on the account id
// create an authentication object to store in context
// set remember-me cookie
myRememberMeServices.onLoginSuccess(
(HttpServletRequest) request.getNativeRequest(),
(HttpServletResponse) request.getNativeResponse(),
authentication);
// forward to the original URL
return extractOriginalUrl(request);
}
source to share
I have a similar requirement.
I tried to use url for authentication like /auth/facebook?_spring_security_remember_me=true
(see AbstractRememberMeServices.rememberMeRequested
). But then org.springframework.social.security.SocialAuthenticationFilter.detectRejection won't skip this pass. It is encoded like this:
protected boolean detectRejection(HttpServletRequest request) {
Set<?> parameterKeys = request.getParameterMap().keySet();
return parameterKeys.size() > 0
&& !parameterKeys.contains("oauth_token")
&& !parameterKeys.contains("code")
&& !parameterKeys.contains("scope");
}
I think if we can add another article in there, it might work. Then I decided to override it by subclassing SocialAuthenticationFilter
. But then SpringSocialConfigurer
it is not entered into, but determined using a keyword new
. Subclassing SpringSocialConfigurer
also doesn't seem like a solution, because there are many useful private fields inside. So, I think the solution is to just copy SpringSocialConfigurer
to another class and use it along with the subclass SocialAuthenticationFilter
. If I got it right, this is all like a hack, and I think we should create a ticket to properly memorize support.
Of course, if we want me to always be in the know, it's easy by setting the field alwaysRemember
to RememberMeServices
, and I do it like this:
@Bean
public RememberMeServices rememberMeServices() {
TokenBasedRememberMeServices rememberMeServices = new TokenBasedRememberMeServices(rememberMeKey, userService);
rememberMeServices.setAlwaysRemember(true);
return rememberMeServices;
}
source to share
So here's what I did to integrate RememberMeServices
with Spring social
. Just like @Sanjay says its based on logic AlwaysRemember=true
.
Instead of the standard one, TokenBasedRememberMeServices
we customize a little:
public class DynamicRememberMeServices extends TokenBasedRememberMeServices {
public final static String PARAM_BASED_KEY = "remember-me-param-based";
public final static String REMEMBER_ME_KEY = "remember-me";
private Logger logger = LoggerFactory.getLogger(getClass());
public DynamicRememberMeServices(String key, UserDetailsService userDetailsService){
super(key, userDetailsService);
super.setParameter(REMEMBER_ME_KEY);
super.setCookieName(REMEMBER_ME_KEY);
}
@Override
protected boolean rememberMeRequested(HttpServletRequest request, String parameter) {
if("on".equalsIgnoreCase(request.getParameter(PARAM_BASED_KEY)) ){
logger.debug("param based request");
return super.rememberMeRequested(request, parameter);
}
logger.debug("always remember me");
return true;
}
}
and on login.html
:
<form th:action="@{/login}" method="post">
User Name : <input type="text" name="username"/>
Password: <input type="password" name="password"/>
<input type="hidden" name="remember-me-param-based" value="on" />
Remember me: <input type='checkbox' name='remember-me' checked="checked"/>
<input type="hidden" name="_csrf" th:value="${_csrf.token}"/>
<input type="submit" value="Sign In"/>
</form>
<a th:href="@{/auth/facebook}">Or via facebook</a>
So in case of facebook login (via /auth/facebook
), it will use the strategy AlwaysRememberMe
by returning true
, but for traditional form-login
it depends:
- If
remember-me-param-based=on
from the request body, it will check the continuation as traditionalTokenBasedRememberMeServices
based on the given parameter (remember-me
). - If
remember-me-param-based
missing, it works likeAlwaysRememberMe
.
IMHO my implementation at least gives a choice for "regular login" users.
source to share
To answer this question, the original way to request it (with XML configuration), Spring Security 3.2+ supports the services-ref attribute on the remember-me element. Therefore, in your security configuration, you would:
<security:http xmlns="http://www.springframework.org/schema/security">
...
<remember-me services-ref="rememberMeServices" key="secret-key">
</security:http>
<bean id="rememberMeServices" class="com.example.MyRememberMeServices">
<constructor-arg index="0" value="secret-key" />
<constructor-arg index="1" ref="userDetailsService" />
<property name="tokenValiditySeconds" value="1000000" />
</bean>
where MyRememberMeServices can be the checklist version or beku8 DynamicRememberMeServices. I prefer the beku8 version as it leaves the option to enable mem-me for regular logins.
source to share