mirror of
https://github.com/DSpace/DSpace.git
synced 2025-10-17 23:13:10 +00:00
Minor code refactoring. Ensure auth cookie invalidation resets CSRF token & that it happens immediately on first usage of cookie
This commit is contained in:
@@ -71,8 +71,7 @@ public interface RestAuthenticationService {
|
|||||||
* existing authentication data/token is destroyed/invalidated and cannot be reused in later requests.
|
* existing authentication data/token is destroyed/invalidated and cannot be reused in later requests.
|
||||||
* <P>
|
* <P>
|
||||||
* In other words, this method invalidates the authentication data created by addAuthenticationDataForUser().
|
* In other words, this method invalidates the authentication data created by addAuthenticationDataForUser().
|
||||||
* This also should include clearing any Cookie created by that method, usually by calling the separate
|
*
|
||||||
* invalidateAuthenticationCookie() method in this same class.
|
|
||||||
* @param request current request
|
* @param request current request
|
||||||
* @param response current response
|
* @param response current response
|
||||||
* @param context current DSpace Context.
|
* @param context current DSpace Context.
|
||||||
@@ -103,8 +102,9 @@ public interface RestAuthenticationService {
|
|||||||
* addAuthenticationDataForUser()). It's useful for those services to immediately *remove/discard* the Cookie after
|
* addAuthenticationDataForUser()). It's useful for those services to immediately *remove/discard* the Cookie after
|
||||||
* it has been used. This ensures the auth Cookie is temporary in nature, and is destroyed as soon as it is no
|
* it has been used. This ensures the auth Cookie is temporary in nature, and is destroyed as soon as it is no
|
||||||
* longer needed.
|
* longer needed.
|
||||||
|
* @param request current request
|
||||||
* @param res current response (where Cookie should be destroyed)
|
* @param res current response (where Cookie should be destroyed)
|
||||||
*/
|
*/
|
||||||
void invalidateAuthenticationCookie(HttpServletResponse res);
|
void invalidateAuthenticationCookie(HttpServletRequest request, HttpServletResponse res);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -39,7 +39,8 @@ import org.springframework.security.web.authentication.www.BasicAuthenticationFi
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom Spring authentication filter for Stateless authentication, intercepts requests to check for valid
|
* Custom Spring authentication filter for Stateless authentication, intercepts requests to check for valid
|
||||||
* authentication
|
* authentication. This runs before *every* request in the DSpace backend to see if any authentication data
|
||||||
|
* is passed in that request. If so, it authenticates the EPerson in the current Context.
|
||||||
*
|
*
|
||||||
* @author Frederic Van Reet (frederic dot vanreet at atmire dot com)
|
* @author Frederic Van Reet (frederic dot vanreet at atmire dot com)
|
||||||
* @author Tom Desair (tom dot desair at atmire dot com)
|
* @author Tom Desair (tom dot desair at atmire dot com)
|
||||||
|
@@ -23,7 +23,10 @@ import org.springframework.security.web.authentication.AbstractAuthenticationPro
|
|||||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class will filter login requests to try and authenticate them
|
* This class will filter /api/authn/login requests to try and authenticate them. Keep in mind, this filter runs *after*
|
||||||
|
* StatelessAuthenticationFilter (which looks for authentication data in the request itself). So, in some scenarios
|
||||||
|
* (e.g. after a Shibboleth login) the StatelessAuthenticationFilter does the actual authentication, and this Filter
|
||||||
|
* just ensures the auth token (JWT) is sent back in an Authorization header.
|
||||||
*
|
*
|
||||||
* @author Frederic Van Reet (frederic dot vanreet at atmire dot com)
|
* @author Frederic Van Reet (frederic dot vanreet at atmire dot com)
|
||||||
* @author Tom Desair (tom dot desair at atmire dot com)
|
* @author Tom Desair (tom dot desair at atmire dot com)
|
||||||
|
@@ -157,7 +157,6 @@ public class JWTTokenRestAuthenticationServiceImpl implements RestAuthentication
|
|||||||
public void invalidateAuthenticationData(HttpServletRequest request, HttpServletResponse response,
|
public void invalidateAuthenticationData(HttpServletRequest request, HttpServletResponse response,
|
||||||
Context context) throws Exception {
|
Context context) throws Exception {
|
||||||
String token = getLoginToken(request, response);
|
String token = getLoginToken(request, response);
|
||||||
invalidateAuthenticationCookie(response);
|
|
||||||
loginJWTTokenHandler.invalidateToken(token, request, context);
|
loginJWTTokenHandler.invalidateToken(token, request, context);
|
||||||
|
|
||||||
// Reset our CSRF token, generating a new one
|
// Reset our CSRF token, generating a new one
|
||||||
@@ -166,16 +165,20 @@ public class JWTTokenRestAuthenticationServiceImpl implements RestAuthentication
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Invalidate our temporary authentication cookie by overwriting it in the response.
|
* Invalidate our temporary authentication cookie by overwriting it in the response.
|
||||||
|
* @param request
|
||||||
* @param response
|
* @param response
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void invalidateAuthenticationCookie(HttpServletResponse response) {
|
public void invalidateAuthenticationCookie(HttpServletRequest request, HttpServletResponse response) {
|
||||||
// Re-send the same cookie (as addTokenToResponse()) with no value and a Max-Age of 0 seconds
|
// Re-send the same cookie (as addTokenToResponse()) with no value and a Max-Age of 0 seconds
|
||||||
ResponseCookie cookie = ResponseCookie.from(AUTHORIZATION_COOKIE, "")
|
ResponseCookie cookie = ResponseCookie.from(AUTHORIZATION_COOKIE, "")
|
||||||
.maxAge(0).httpOnly(true).secure(true).sameSite("None").build();
|
.maxAge(0).httpOnly(true).secure(true).sameSite("None").build();
|
||||||
|
|
||||||
// Write the cookie to the Set-Cookie header in order to send it
|
// Write the cookie to the Set-Cookie header in order to send it
|
||||||
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
|
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
|
||||||
|
|
||||||
|
// Reset our CSRF token, generating a new one
|
||||||
|
resetCSRFToken(request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -254,7 +257,6 @@ public class JWTTokenRestAuthenticationServiceImpl implements RestAuthentication
|
|||||||
* Get the Login token (JWT) in the current request. First we check the Authorization header.
|
* Get the Login token (JWT) in the current request. First we check the Authorization header.
|
||||||
* If not found there, we check for a temporary authentication cookie and use that.
|
* If not found there, we check for a temporary authentication cookie and use that.
|
||||||
* @param request current request
|
* @param request current request
|
||||||
* @param request current response
|
|
||||||
* @return authentication token (if found), or null
|
* @return authentication token (if found), or null
|
||||||
*/
|
*/
|
||||||
private String getLoginToken(HttpServletRequest request, HttpServletResponse response) {
|
private String getLoginToken(HttpServletRequest request, HttpServletResponse response) {
|
||||||
@@ -265,12 +267,10 @@ public class JWTTokenRestAuthenticationServiceImpl implements RestAuthentication
|
|||||||
tokenValue = authHeader.replace(AUTHORIZATION_TYPE, "").trim();
|
tokenValue = authHeader.replace(AUTHORIZATION_TYPE, "").trim();
|
||||||
} else if (StringUtils.isNotBlank(authCookie)) {
|
} else if (StringUtils.isNotBlank(authCookie)) {
|
||||||
tokenValue = authCookie;
|
tokenValue = authCookie;
|
||||||
// If auth cookie is read/used, then we immediately remove the cookie & reset the CSRF token.
|
// After auth cookie is read, immediately invalidate it (and this also resets the CSRF token).
|
||||||
// This is because we treat auth cookies as temporary in nature, only to be used once & only used by
|
// This ensures the auth cookie is temporary in nature, only to be used once (usually on initial login,
|
||||||
// specific auth plugins (like Shibboleth) which require them. So, an auth cookie should only ever be
|
// e.g. via Shibboleth). After that, we assume Authorization Header will be used.
|
||||||
// used for an initial login, after which Headers are used.
|
invalidateAuthenticationCookie(request, response);
|
||||||
invalidateAuthenticationCookie(response);
|
|
||||||
resetCSRFToken(request, response);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return tokenValue;
|
return tokenValue;
|
||||||
@@ -292,6 +292,7 @@ public class JWTTokenRestAuthenticationServiceImpl implements RestAuthentication
|
|||||||
for (Cookie cookie : cookies) {
|
for (Cookie cookie : cookies) {
|
||||||
if (cookie.getName().equals(AUTHORIZATION_COOKIE) && StringUtils.isNotEmpty(cookie.getValue())) {
|
if (cookie.getName().equals(AUTHORIZATION_COOKIE) && StringUtils.isNotEmpty(cookie.getValue())) {
|
||||||
authCookie = cookie.getValue();
|
authCookie = cookie.getValue();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user