Complete code for a Spring OAuth2 implementation of Multi-Factor Authentication has been uploaded to a file sharing site at this link. Instructions are given below to recreate the current problem on any computer in only a few minutes.
**CURRENT PROBLEM:**
Most of the authentication algorithm works correctly. The program does not break until the very end of the control flow shown below. Specifically, an `Invalid CSRF token found for http://localhost:9999/uaa/oauth/token` error is being thrown at the end of the **SECOND PASS** below. The app in the link above was developed by adding a custom `OAuth2RequestFactory`, `TwoFactorAuthenticationFilter` and `TwoFactorAuthenticationController` to the `authserver` app of this Spring Boot OAuth2 GitHub sample. **What specific changes need to be made to the code below in order to resolve this CSRF token error and enable 2-factor authentication?**
My research leads me to suspect that the `CustomOAuth2RequestFactory` (API at this link) might be the place to configure a solution because it defines ways for managing `AuthorizationRequest`s and `TokenRequest`s.
**This section of the official OAuth2 spec indicates that the `state` parameter of the request made to the authorization endpoint is the place where the `csrf` token is added.**
Also, the code in the link uses the Authorization Code Grant Type described at this link to the official spec, which would mean that Step C in the flow does not update the `csrf` code, thus triggering the error in Step D. (You can view the entire flow including Step C and Step D in the official spec.)
**CONTROL FLOW SURROUNDING THE CURRENT ERROR:**
The current error is being thrown during the **SECOND PASS** through `TwoFactorAuthenticationFilter` in the flowchart below. Everything works as intended until the control flow gets into the **SECOND PASS**.
The following flowchart illustrates the control flow of the two factor authentication process that is employed by the code in the downloadable app.
Specifically, the Firefox `HTTP` Headers for the sequence of `POST`s and `GET`s show that the same `XSRF` cookie is sent with every request in the sequence. The `XSRF` token values do not cause a problem until after the `POST /secure/two_factor_authentication`, which triggers server processing at the `/oauth/authorize` and `/oauth/token` endpoints, with `/oauth/token` throwing the `Invalid CSRF token found for http://localhost:9999/uaa/oauth/token` error.
To understand the relationship between the above control flow chart and the `/oauth/authorize` and `/oauth/token` endpoints, you can compare the above flowchart side by side with the chart for the single factor flow at the official spec in a separate browser window. The **SECOND PASS** above simply runs through the steps from the one-factor official spec a second time, but with greater permissions during the **SECOND PASS**.
**WHAT THE LOGS SAY:**
The HTTP Request and Response Headers indicate that:
1.) A POST to `9999/login` with the correct `username` and `password` submitted results in a redirect to `9999/authorize?client_id=acme&redirect_uri=/login&response_type=code&state=sGXQ4v` followed by a `GET 9999/secure/two_factor_authenticated`. One XSRF token remains constant across these exchanges.
2.) A POST to `9999/secure/two_factor_authentication` with the correct pin code sends the same `XSRF` token, and gets successfully re-directed to `POST 9999/oauth/authorize` and makes it into `TwoFactorAuthenticationFilter.doFilterInternal()` and proceeds to `request 9999/oauth/token`, but `9999/oauth/token` rejects the request because the same old XSRF token does not match a new `XSRF` token value, which was apparently created during the **FIRST PASS**.
One obvious difference between `1.)` and `2.)` is that the second `request 9999/oauth/authorize` in `2.)` does not contain the url parameters which are included in the first request to `9999/authorize?client_id=acme&redirect_uri=/login&response_type=code&state=sGXQ4v` in `1.)`, and also defined in the official spec. But it is not clear if this is causing the problem.
Also, it is not clear how to access the parameters to send a fully formed request from the `TwoFactorAuthenticationController.POST`. I did a SYSO of the `parameters` `Map` in the `HttpServletRequest` for the `POST 9999/secure/two_factor_authentication` controller method, and all it contains are the `pinVal` and `_csrf` variables.
You can read all the HTTP Headers and Spring Boot logs at a file sharing site by clicking on this link.
**A FAILED APPROACH:**
I tried #RobWinch's approach to a similar problem in the Spring Security 3.2 environment, but the approach does not seem to apply to the context of Spring OAuth2. Specifically, when the following `XSRF` update code block is uncommented in the `TwoFactorAuthenticationFilter` code shown below, the downstream request headers do show a different/new `XSRF` token value, but the same error is thrown.
if(AuthenticationUtil.hasAuthority(ROLE_TWO_FACTOR_AUTHENTICATED)){
CsrfToken token = (CsrfToken) request.getAttribute("_csrf");
response.setHeader("XSRF-TOKEN"/*"X-CSRF-TOKEN"*/, token.getToken());
}
**This indicates that the `XSRF` configuration needs to be updated in a way that `/oauth/authorize` and `/oauth/token` are able to talk with each other and with the client and resource apps to successfully manage the `XSRF` token values.** Perhaps the `CustomOAuth2RequestFactory` is what needs to be changed to accomplish this. But how?
**RELEVANT CODE:**
The code for `CustomOAuth2RequestFactory` is:
public class CustomOAuth2RequestFactory extends DefaultOAuth2RequestFactory {
public static final String SAVED_AUTHORIZATION_REQUEST_SESSION_ATTRIBUTE_NAME = "savedAuthorizationRequest";
public CustomOAuth2RequestFactory(ClientDetailsService clientDetailsService) {
super(clientDetailsService);
}
#Override
public AuthorizationRequest createAuthorizationRequest(Map authorizationParameters) {
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
HttpSession session = attr.getRequest().getSession(false);
if (session != null) {
AuthorizationRequest authorizationRequest = (AuthorizationRequest) session.getAttribute(SAVED_AUTHORIZATION_REQUEST_SESSION_ATTRIBUTE_NAME);
if (authorizationRequest != null) {
session.removeAttribute(SAVED_AUTHORIZATION_REQUEST_SESSION_ATTRIBUTE_NAME);
return authorizationRequest;
}
}
return super.createAuthorizationRequest(authorizationParameters);
}
}
The code for `TwoFactorAuthenticationFilter` is:
//This class is added per: https://stackoverflow.com/questions/30319666/two-factor-authentication-with-spring-security-oauth2
/**
* Stores the oauth authorizationRequest in the session so that it can
* later be picked by the {#link com.example.CustomOAuth2RequestFactory}
* to continue with the authoriztion flow.
*/
public class TwoFactorAuthenticationFilter extends OncePerRequestFilter {
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
private OAuth2RequestFactory oAuth2RequestFactory;
//These next two are added as a test to avoid the compilation errors that happened when they were not defined.
public static final String ROLE_TWO_FACTOR_AUTHENTICATED = "ROLE_TWO_FACTOR_AUTHENTICATED";
public static final String ROLE_TWO_FACTOR_AUTHENTICATION_ENABLED = "ROLE_TWO_FACTOR_AUTHENTICATION_ENABLED";
#Autowired
public void setClientDetailsService(ClientDetailsService clientDetailsService) {
oAuth2RequestFactory = new DefaultOAuth2RequestFactory(clientDetailsService);
}
private boolean twoFactorAuthenticationEnabled(Collection authorities) {
System.out.println(">>>>>>>>>>> List of authorities includes: ");
for (GrantedAuthority authority : authorities) {
System.out.println("auth: "+authority.getAuthority() );
}
return authorities.stream().anyMatch(
authority -> ROLE_TWO_FACTOR_AUTHENTICATION_ENABLED.equals(authority.getAuthority())
);
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
System.out.println("------------------ INSIDE TwoFactorAuthenticationFilter.doFilterInternal() ------------------------");
// Check if the user hasn't done the two factor authentication.
if (AuthenticationUtil.isAuthenticated() && !AuthenticationUtil.hasAuthority(ROLE_TWO_FACTOR_AUTHENTICATED)) {
System.out.println("++++++++++++++++++++++++ AUTHENTICATED BUT NOT TWO FACTOR +++++++++++++++++++++++++");
AuthorizationRequest authorizationRequest = oAuth2RequestFactory.createAuthorizationRequest(paramsFromRequest(request));
/* Check if the client's authorities (authorizationRequest.getAuthorities()) or the user's ones
require two factor authenticatoin. */
System.out.println("======================== twoFactorAuthenticationEnabled(authorizationRequest.getAuthorities()) is: " + twoFactorAuthenticationEnabled(authorizationRequest.getAuthorities()) );
System.out.println("======================== twoFactorAuthenticationEnabled(SecurityContextHolder.getContext().getAuthentication().getAuthorities()) is: " + twoFactorAuthenticationEnabled(SecurityContextHolder.getContext().getAuthentication().getAuthorities()) );
if (twoFactorAuthenticationEnabled(authorizationRequest.getAuthorities()) ||
twoFactorAuthenticationEnabled(SecurityContextHolder.getContext().getAuthentication().getAuthorities())) {
// Save the authorizationRequest in the session. This allows the CustomOAuth2RequestFactory
// to return this saved request to the AuthenticationEndpoint after the user successfully
// did the two factor authentication.
request.getSession().setAttribute(CustomOAuth2RequestFactory.SAVED_AUTHORIZATION_REQUEST_SESSION_ATTRIBUTE_NAME, authorizationRequest);
// redirect the the page where the user needs to enter the two factor authentiation code
redirectStrategy.sendRedirect(request, response,
ServletUriComponentsBuilder.fromCurrentContextPath()
.path(TwoFactorAuthenticationController.PATH)
.toUriString());
return;
}
}
//THE NEXT "IF" BLOCK DOES NOT RESOLVE THE ERROR WHEN UNCOMMENTED
//if(AuthenticationUtil.hasAuthority(ROLE_TWO_FACTOR_AUTHENTICATED)){
// CsrfToken token = (CsrfToken) request.getAttribute("_csrf");
// this is the value of the token to be included as either a header or an HTTP parameter
// response.setHeader("XSRF-TOKEN", token.getToken());
//}
filterChain.doFilter(request, response);
}
private Map paramsFromRequest(HttpServletRequest request) {
Map params = new HashMap();
for (Entry entry : request.getParameterMap().entrySet()) {
params.put(entry.getKey(), entry.getValue()[0]);
}
return params;
}
}
**RE-CREATING THE PROBLEM ON YOUR COMPUTER:**
You can recreate the problem on any computer in only a few minutes by following these simple steps:
1.) Download the zipped version of the app from a file sharing site by clicking on this link.
2.) Unzip the app by typing: `tar -zxvf oauth2.tar(2).gz`
3.) launch the `authserver` app by navigating to `oauth2/authserver` and then typing `mvn spring-boot:run`.
4.) launch the `resource` app by navigating to `oauth2/resource` and then typing `mvn spring-boot:run`
5.) launch the `ui` app by navigating to `oauth2/ui` and then typing `mvn spring-boot:run`
6.) Open a web browser and navigate to `http : // localhost : 8080`
7.) Click `Login` and then enter `Frodo` as the user and `MyRing` as the password, and click to submit.
8.) Enter `5309` as the `Pin Code` and click submit. **This will trigger the error shown above.**
You can view the complete source code by:
a.) importing the maven projects into your IDE, or by
b.) navigating within the unzipped directories and opening with a text editor.
You can read all the HTTP Headers and Spring Boot logs at a file sharing site by clicking on this link.
One idea that popped to my head:
If session fixation is activated, a new session is created after the user authenticated successfully (see SessionFixationProtectionStrategy). This will also of course create a new csrf token if you use the default HttpSessionCsrfTokenRepository. Since you're mentioning the XSRF-TOKEN header I assume you use some JavaScript frontend. I could imagine that the original csrf token that was used for the login is stored and reused afterwards - which would not work because this csrf token is not valid anymore.
You may try disabling session fixation (http.sessionManagement().sessionFixation().none() or <session-management session-fixation-protection="none"/>) or re-get the current CSRF token after login.
Your CustomOAuth2RequestFactory is putting the previous request in-place of the current request. However, you are not updating the XSRF token in the old request when you make this switch. Here is what I would suggest for the updated CustomOAuth2Request:
#Override
public AuthorizationRequest createAuthorizationRequest(Map<String, String> authorizationParameters) {
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
HttpSession session = attr.getRequest().getSession(false);
if (session != null) {
AuthorizationRequest authorizationRequest = (AuthorizationRequest) session.getAttribute(SAVED_AUTHORIZATION_REQUEST_SESSION_ATTRIBUTE_NAME);
if (authorizationRequest != null) {
session.removeAttribute(SAVED_AUTHORIZATION_REQUEST_SESSION_ATTRIBUTE_NAME);
//UPDATE THE STATE VARIABLE WITH THE NEW TOKEN. THIS PART IS NEW
CsrfToken csrf = (CsrfToken) attr.getRequest().getAttribute(CsrfToken.class.getName());
String attrToken = csrf.getToken();
authorizationRequest.setState(attrToken);
return authorizationRequest;
}
}
return super.createAuthorizationRequest(authorizationParameters);
}
I am revisiting this because my initial answer draft got downvoted. This version is further along the same path, which I believe is the right avenue of approach.
Related
I am using liferay and I have a spring portlet.
The view is rendered fine. I have a link to the same page - if clicked, it should perform some logic and then (conditionaly) open a new tab with a external site:
What I have so far:
#Component
#RequestMapping(value = "VIEW")
...
#RenderMapping
public String view(Model model, PortletRequest request, PortletResponse response) throws Exception {
....
response.setProperty(ResourceResponse.HTTP_STATUS_CODE, String.valueOf(HttpServletResponse.SC_MOVED_TEMPORARILY));
final String redirect = "https://......";
response.setProperty("Location", redirect);
return "redirect:" + redirect;
.....
It does open a new tab, but it does not leave the portal context. It wont open the new location.
Any hints?
Given that you have access to try the following:
#RenderMapping
public String view(Model model, PortletRequest request, HttpServletResponse response) throws Exception {
final String redirect = "https://......";
response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
response.setHeader("Location", redirect);
return "";
}
Rendering a portlet always renders part of a page in a portal, thus you won't have access to the original HttpServletResponse without trickery. And even if you get access, the response might already be committed, the portal engine might have decided to deliver your content asynchronously - you're well shielded from the actual result. in short: you can't really think along any HTTP games.
You have a couple of other options though:
render JS that does the redirect
move your code to the portlet's Action phase, where state updates are expected (render phase is supposed to render, not to change state or make such decisions) - in the action phase you could issue a redirect, but it'll be hard to conditionally do so for opening in a new tab
move your code to the portlet's resource phase, trigger it asynchronously and decide in the frontend if you want to open a new tab or not, based on the return you get.
move your code to a REST service (continue as in the resource phase bullet point above)
Here is my spring login handler
public class LoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
HttpSession session = request.getSession();
LoggedInUser logginUserObj = (LoggedInUser)authentication.getPrincipal();
String userId = logginUserObj.getUserId();
session.setAttribute("userId", userId);
session.setAttribute("userName", logginUserObj.getName());
session.setAttribute("hasPhoto", user.getProperty(UserVertex.profilePhotoUploaded.toString()));
if(logginUserObj.getActivateLink()!=null)
session.setAttribute("activateLink", logginUserObj.getActivateLink());
try {
Object referUrl = session.getAttribute("Referer_url");
if(referUrl != null){
session.removeAttribute("Referer_url");
response.sendRedirect(referUrl.toString());
}
else
response.sendRedirect("profile/"+userId);
} catch (IOException ex) {
logger.error(ex);
}
}
}
My client is in plain html, jQuery (in cordova). So my question is if everytime user makes a request, how will I know that requested user is logged in or not ?
e.g. here is a sample :
$.ajax({
url: "addNewpost",
data:{}
})
Here only a loggedin user can add new post. So when i am calling addNewpost rest service in spring #RestController how will i know that request is from logged in user ?
Since you're using a success handler so before the request goes to success handler it goes to authentication manager.In authentication manager if you have configured any authentication provider there authenticity of the user will be check and if it returns success then the call will go to onAuthenticationSuccess.In the authentication provider you actually look for some values in the request header depending upon the type of authentication provider you are using.If its missing then the user is not logged in.Here is a better picture with one example(Here its also checking authorization for admin)
You can also refer to this link
Hope this gives you a clear picture!!
First you have to make sure that your web app returns 401 unauthorized when you are not logged in
Then with JQuery you use the error callback more or less as in following snippet, usually checking that jqXHR.status is 401 (unauthorized)
$.ajax({url:url, data:{}, error: function(jqXHR, e, x){
alert("Error #" + jqXHR.status + "\n\n\t" + x );
});
Whether the request is from a logged in user, in the server, depends on your security configuration, you should not check in the controller "manually" but just be sure that configuration declarations are correct.
BTW not sure if is the case to redirect to ANY url after login
Hi I have been unable to solve the following problem in Wicket 6.*:
In our webapp we are using wicket-auth-roles to manage authentication/authorization. When session expires, user should be redirected to a page set by getApplicationSettings().setPageExpiredErrorPage(SomePage.class) on his next action. However, if the user tries to access a page which doesn't allow guests, he is redirected to a login page skipping the PageExpiredPage altogether.
My question is - how can I display "Session has expired." message to the user?
Among other things, I have tried session.info("message") during onInvalidate phase of session's lifecycle, however the feedback message is then rendered on the first page after login (not on the login page).
Thank you for your anwsers.
You could use a RequestCycleListener to record when a PageExpiredException is thrown.
public class ExceptionMapperListener extends AbstractRequestCycleListener {
#Override
public IRequestHandler onException(RequestCycle cycle, Exception ex) {
if (ex instanceof PageExpiredException) {
// Record in session or request cycle
// OR
// Create a RenderPageRequestHandler yourself and add a page parameter
// See DefaultExceptionMapper#internalMap(Exception)
}
return null;
}
}
// In Application#init():
getRequestCycleListeners().add(new ExceptionMapperListener());
ORINAL ANSWER
(kept because it could still help...)
I haven't tried it myself since I don't use wicket-auth-roles, but try overriding the method AuthenticatedWebApplication#restartResponseAtSignInPage() with something like this:
if (isSessionExpired()) {
PageParameters params = new PageParameters();
params.add("showSessionExpired", true);
throw new RestartResponseAtInterceptPageException(getSignInPageClass(), params);
} else {
throw new RestartResponseAtInterceptPageException(getSignInPageClass());
}
And then in the SignInPageClass, display the desired message if the showSessionExpired page parameter is present.
I'm not sure how you implement isSessionExpired(), but you seem to have that part already covered.
OR
Depending on how you implemented isSessionExpired(), maybe you could do the following in your SignInPageClass:
if (sessionExpired()) {
session.info("message")
}
After bernie put me on the right path, I eventually figured out a solution to the problem:
First it is required to override RequestCycleListener:
public class SessionExpiredListener extends AbstractRequestCycleListener {
public void onRequestHandlerResolved(RequestCycle cycle, IRequestHandler handler) {
if (handler instanceof IPageRequestHandler) {
IPageRequestHandler pageHandler = (IPageRequestHandler) handler;
HttpServletRequest request = (HttpServletRequest) cycle.getRequest().getContainerRequest();
//check whether the requested session has expired
boolean expired = request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid();
//check whether the requested page can be instantiated with the current session
boolean authorized = Session.get().getAuthorizationStrategy().isInstantiationAuthorized(pageHandler.getPageClass());
if (expired && !authorized) {
throw new PageExpiredException("Session has expired!");
}
}
super.onRequestHandlerResolved(cycle, handler);
}
}
Check for authorized prevents the session-expired message from displaying on log-out or when accessing unprotected pages.
Finally, you must register your listener and PageRequestHandlerTracker in your WebApplication:
getRequestCycleListeners().add(new SessionExpiredListener());
getRequestCycleListeners().add(new PageRequestHandlerTracker());
We have a page where User can come and create his ID. So any unauthorized user can come and create his ID.
Problem is this page is having some AJAX calls for validation which checks if ID format which user is entering on screen is correct or not.
An attacker can note down the AJAX based URL through Browser-> Inspect Element and can choke our server calling it multiple times through some attacking tools.
Please note AJAX based URL is hitting to a web service which in my opinion is doing resource intensive operation(i.e domain is correct or not, user already exist or not?).
I am using Spring MVC as web application framework. Can I protect direct access of URL (ajax) for an unauthorized user?
You can't hide the URL of the web-service if it's being requested by AJAX (XmlHTTPrequest).
You'd be best to implement server-side "throttling" (google it!) on the webserver that serves your web-service. If a particular IP makes too many requests, or some overall threshhold of requests is passed -- you return a web-service exception that asks the user to try again later.
In the case of a DDOS, then you'll need to deal with that at the network routing level.
I have a similar requirement, of protecting my Ajax Resources - Not to be called from the Browser Addressbar, but via a AJAX request, basically a XMLHTTPRequest.
Wrote a AjaxOnlyFilter which looks for the URL Mappings in a array of Strings, and if matches, checks presence of the "X-Requested-With" header.
If header not present OR value not matching to value "XMLHttpRequest", then invoke forward on requestDispatcher to error page or set 400 status.
private String[] mappings = { "/model", "/records" , "/update" , "/insert", "/delete"};
public boolean urlContainsMappingsFromAJAXList(String url)
{
for(int i =0; i < mappings.length; i++)
{
if(url.contains(mappings[i]))
{
return true;
}
}
return false;
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
StringBuffer requestURL = httpServletRequest.getRequestURL();
if(urlContainsMappingsFromAJAXList(requestURL.toString())){
String requestedWithHeader = httpServletRequest.getHeader("X-Requested-With");
//if X-Requested-With header is not XMLHttpRequest
if(requestedWithHeader==null || (!requestedWithHeader.equalsIgnoreCase("xmlhttprequest"))){
LOGGER.debug("Not a AJAX request, redirection to error page");
httpServletRequest.getRequestDispatcher("error404.jsp").forward(request, response);
return;
}
//else continue with filter chain
}
//else continue with filter chain
// pass the request along the filter chain
filterChain.doFilter(request, response);
}
I am using Spring Security and wondering how can I implement redirection after succesfull login to the source page if that page contains # (hash) sign.
Right now I use always-use-default-target="false" and it works fine on URL kind of: /path/to/page/.
But when the URL become to #/path/to/page it doesn't make any redirections.
Is there any way to fix it?
Here is the solution I used at the end:
$(document).ready(function(){
$('#auth-form').submit(function() {
var el = $(this);
var hash = window.location.hash;
if (hash) el.prop('action', el.prop('action') + '#' + unescape(hash.substring(1)));
return true;
});
});
This snippet addes the hash to authorization form's action attribute and Spring redirect you to the URL of kind: #/path/to/page without any problem.
Maybe this is the old question, but during my recent research in this topic, I found that the problem is common and still exists (especially in case of modern AngularJS front-end apps with back-end security). I'd like to share my solution with you.
On the login page, e.g., /login.html, put following code before the </body> tag:
<script type="text/javascript">
var hash = window.location.hash;
document.cookie="hashPart=" + window.btoa(hash);
</script>
Note (1): btoa() function works in IE >= 10 (http://www.w3schools.com/jsref/met_win_btoa.asp), for older browsers use jQuery equivalent.
Note (2): The encryption of the # part of URL is necessary as it may contain special characters, which are not allowed to be stored in cookie value string.
From the server side you have to modify onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) method of class implementing AuthenticationSuccessHandler interface.
In my case, I simply extend the SavedRequestAwareAuthenticationSuccessHandler class and override the onAuthenticationSuccess method using its original code. Then I obtain the hashPart cookie value from the request, decode it and add to resolved redirect URL. My code fragment below:
#Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws ServletException, IOException {
// ... copy/paste original implementation here, until ...
// Use the DefaultSavedRequest URL
String targetUrl = savedRequest.getRedirectUrl();
for (Cookie cookie : req.getCookies()) {
if (cookie.getName().equals("hashPart")) {
targetUrl += new String(Base64Utils.decodeFromString(cookie.getValue()));
cookie.setMaxAge(0); // clear cookie as no longer needed
response.addCookie(cookie);
break;
}
}
getRedirectStrategy().sendRedirect(request, response, targetUrl);
}
Finally, just inject your success handler class to your Spring Security configuration, as described in: https://stackoverflow.com/a/21100458/3076403
I'm looking forward to your comments or other solutions to this problem.