/**
* Interface for handling authentication operations.
*
* @param <T> Type of login request (extends AuthRequest)
* @param <R> Type of login response (extends AuthResponse)
* @param <U> Type of registration request (extends RegisterRequest)
* @param <V> Type of registration response (extends RegisterResponse)
*/
public interface AuthenticationService<T extends AuthRequest, R extends AuthResponse, U extends RegisterRequest, V extends RegisterResponse> {
/**
* Authenticates a user and returns an auth response.
*
* @param loginRequest Login credentials
* @param response HttpServletResponse (used for setting fingerprint cookie)
* @param issuerId Identifier for the token issuer
* @return AuthResponse containing the JWT token
*/
R authenticateUser(T loginRequest, HttpServletResponse response, String issuerId);
/**
* Registers a new user.
*
* @param registrationRequest Registration data
* @return RegisterResponse containing registration result or user data
*/
V registerUser(U registrationRequest);
/**
* Logs out the user by revoking the JWT and deleting the cookie.
*
* @param jwtToken Token to be revoked
* @param response HttpServletResponse (used for cookie deletion)
* @param cookieName Name of the cookie to delete
* @return RegisterResponse with logout message
*/
V logout(String jwtToken, HttpServletResponse response, String cookieName);
}
@RequiredArgsConstructor
public abstract class DefaultAuthenticationService<
T extends AuthRequest,
R extends AuthResponse,
U extends RegisterRequest,
V extends RegisterResponse>
implements AuthenticationService<T, R, U, V> {
private static final Logger logger = LoggerFactory.getLogger(DefaultAuthenticationService.class);
private final AuthenticationManager authenticationManager;
private final TokenCipher tokenCipher;
private final TokenRevoker tokenRevoker;
private final Supplier<R> authResponseSupplier;
private final Supplier<V> registerResponseSupplier;
private final CookieProvider cookieProvider;
private final JwtTokenProvider jwtTokenProvider;
private final ApplicationEventPublisher eventPublisher;
private final FingerprintUtils fingerprintUtils;
@Override
public R authenticateUser(T loginRequest, HttpServletResponse response, String issuerId) {
try {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
loginRequest.getIdentifier(),
loginRequest.getPassword()
)
);
SecurityContextHolder.getContext().setAuthentication(authentication);
String fingerprint = fingerprintUtils.generateFingerprint();
logger.debug("Generated fingerprint: {}", fingerprint);
cookieProvider.setFingerprintCookie(response, fingerprint);
String fingerprintHash = FingerprintUtils.hashFingerprint(fingerprint);
logger.debug("Generated fingerprint hash: {}", fingerprintHash);
String jwt = jwtTokenProvider.generateToken(loginRequest.getIdentifier(), fingerprintHash);
String cipheredJwt = tokenCipher.cipherToken(jwt);
logger.debug("Generated ciphered token: {}", cipheredJwt);
R authResponse = authResponseSupplier.get();
authResponse.setToken(cipheredJwt);
eventPublisher.publishEvent(
new AuthSuccessEvent(this, loginRequest.getIdentifier())
);
return authResponse;
} catch (AuthenticationException e) {
logger.warn("Auth failure for {}", loginRequest.getIdentifier());
throw new AuthenticationFailureException("Invalid credentials", e);
} catch (TokenEncyptionException e) {
logger.error("Token security breach", e);
throw new ServiceSecurityException("Token processing failure", e);
} catch (Exception e) {
logger.error("System authentication failure", e);
throw new AuthServiceException("Global auth failure", e);
}
}
@Override
public V registerUser(U registrationRequest) {
throw new UnsupportedOperationException("registerUser must be overridden in subclass");
}
@Override
public V logout(String jwtToken, HttpServletResponse response, String cookieName) {
try {
cookieProvider.deleteCookie(response, cookieName);
tokenRevoker.revokeToken(jwtToken);
V registerResponse = registerResponseSupplier.get();
registerResponse.setMessage("Logged out successfully!");
return registerResponse;
} catch (TokenRevocationException e) {
logger.error("Error during logout", e);
V registerResponse = registerResponseSupplier.get();
registerResponse.setMessage("Error during logout: " + e.getMessage());
return registerResponse;
}
catch (Exception e) {
logger.error("System logout failure", e);
V registerResponse = registerResponseSupplier.get();
registerResponse.setMessage("System logout failure: " + e.getMessage());
return registerResponse;
}
}
}