108 lines
4.3 KiB
Java
108 lines
4.3 KiB
Java
package app.mealsmadeeasy.api.auth;
|
|
|
|
import app.mealsmadeeasy.api.jwt.JwtService;
|
|
import app.mealsmadeeasy.api.user.User;
|
|
import jakarta.transaction.Transactional;
|
|
import org.jetbrains.annotations.Nullable;
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
import org.springframework.scheduling.annotation.Scheduled;
|
|
import org.springframework.security.authentication.AuthenticationManager;
|
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
|
import org.springframework.security.core.Authentication;
|
|
import org.springframework.security.core.AuthenticationException;
|
|
import org.springframework.stereotype.Service;
|
|
|
|
import java.time.OffsetDateTime;
|
|
import java.util.UUID;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
@Service
|
|
public class AuthServiceImpl implements AuthService {
|
|
|
|
private final AuthenticationManager authenticationManager;
|
|
private final JwtService jwtService;
|
|
private final RefreshTokenRepository refreshTokenRepository;
|
|
private final long refreshTokenLifetime;
|
|
|
|
public AuthServiceImpl(
|
|
AuthenticationManager authenticationManager,
|
|
JwtService jwtService,
|
|
RefreshTokenRepository refreshTokenRepository,
|
|
@Value("${app.mealsmadeeasy.api.security.refresh-token-lifetime}") Long refreshTokenLifetime
|
|
) {
|
|
this.authenticationManager = authenticationManager;
|
|
this.jwtService = jwtService;
|
|
this.refreshTokenRepository = refreshTokenRepository;
|
|
this.refreshTokenLifetime = refreshTokenLifetime;
|
|
}
|
|
|
|
private RefreshToken createRefreshToken(User principal) {
|
|
final RefreshToken refreshTokenDraft = new RefreshToken();
|
|
refreshTokenDraft.setToken(UUID.randomUUID());
|
|
refreshTokenDraft.setIssued(OffsetDateTime.now());
|
|
refreshTokenDraft.setExpiration(OffsetDateTime.now().plusSeconds(this.refreshTokenLifetime));
|
|
refreshTokenDraft.setOwner(principal);
|
|
return this.refreshTokenRepository.save(refreshTokenDraft);
|
|
}
|
|
|
|
@Override
|
|
public LoginDetails login(String username, String password) throws LoginException {
|
|
try {
|
|
final Authentication authentication = this.authenticationManager.authenticate(
|
|
new UsernamePasswordAuthenticationToken(username, password)
|
|
);
|
|
final User principal = (User) authentication.getPrincipal();
|
|
return new LoginDetails(
|
|
username,
|
|
this.jwtService.generateAccessToken(username),
|
|
this.createRefreshToken(principal)
|
|
);
|
|
} catch (AuthenticationException e) {
|
|
throw new LoginException(LoginExceptionReason.INVALID_CREDENTIALS, e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
@Transactional
|
|
public void logout(UUID refreshToken) {
|
|
this.refreshTokenRepository.findByToken(refreshToken).ifPresent(this.refreshTokenRepository::delete);
|
|
}
|
|
|
|
@Override
|
|
@Transactional
|
|
public LoginDetails refresh(@Nullable UUID refreshToken) throws LoginException {
|
|
if (refreshToken == null) {
|
|
throw new LoginException(LoginExceptionReason.NO_REFRESH_TOKEN, "No refresh token provided.");
|
|
}
|
|
|
|
final RefreshToken old = this.refreshTokenRepository.findByToken(refreshToken)
|
|
.orElseThrow(() -> new LoginException(
|
|
LoginExceptionReason.INVALID_REFRESH_TOKEN,
|
|
"No such refresh token: " + refreshToken
|
|
));
|
|
if (old.getRevoked() || old.getDeleted()) {
|
|
throw new LoginException(LoginExceptionReason.INVALID_REFRESH_TOKEN, "Invalid refresh token.");
|
|
}
|
|
if (old.getExpiration().isBefore(OffsetDateTime.now())) {
|
|
throw new LoginException(LoginExceptionReason.EXPIRED_REFRESH_TOKEN, "Refresh token is expired.");
|
|
}
|
|
|
|
final User principal = old.getOwner();
|
|
old.setDeleted(true);
|
|
this.refreshTokenRepository.save(old);
|
|
|
|
final String username = principal.getUsername();
|
|
return new LoginDetails(
|
|
username,
|
|
this.jwtService.generateAccessToken(username),
|
|
this.createRefreshToken(principal)
|
|
);
|
|
}
|
|
|
|
@Scheduled(fixedDelay = 60, timeUnit = TimeUnit.SECONDS)
|
|
public void cleanUpDeletedRefreshTokens() {
|
|
this.refreshTokenRepository.deleteAllWhereSoftDeleted();
|
|
}
|
|
|
|
}
|