package com.vega.hrm.service; import com.google.api.client.auth.oauth2.TokenResponse; import com.google.api.client.googleapis.auth.oauth2.GoogleCredential; import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.JsonFactory; import com.google.api.client.json.jackson2.JacksonFactory; import com.vega.hrm.core.component.TokenStore; import com.vega.hrm.core.dto.GoogleOAuthConfig; import com.vega.hrm.core.entities.UserGoogleToken; import com.vega.hrm.core.helpers.LogHelper; import com.vega.hrm.core.repositories.UserGoogleTokenRepository; import java.io.IOException; import java.security.GeneralSecurityException; import java.time.Instant; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service @RequiredArgsConstructor public class TokenRefreshService { private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance(); private final UserGoogleTokenRepository userGoogleTokenRepository; private final GoogleOAuthConfig googleOAuthConfig; private final TokenStore tokenStore; /** * Refresh access token bằng refresh token * @param userGoogleToken Token cần refresh * @return true nếu refresh thành công, false nếu thất bại */ @Transactional public boolean refreshAccessToken(UserGoogleToken userGoogleToken) { if (userGoogleToken == null || userGoogleToken.getRefreshToken() == null) { LogHelper.error("Không thể refresh token: token hoặc refresh token null"); return false; } // Kiểm tra refresh token có còn hiệu lực không if (userGoogleToken.getRefreshTokenExpiresAt() != null && userGoogleToken.getRefreshTokenExpiresAt().isBefore(Instant.now())) { LogHelper.error("Refresh token đã hết hạn cho email: " + userGoogleToken.getEmail()); return false; } NetHttpTransport httpTransport = null; try { httpTransport = GoogleNetHttpTransport.newTrustedTransport(); } catch (GeneralSecurityException | IOException e) { LogHelper.error("Lỗi khi tạo HTTP transport: " + e.getMessage()); return false; } try { // Tạo GoogleCredential với refresh token để tự động refresh GoogleCredential credential = new GoogleCredential.Builder() .setTransport(httpTransport) .setJsonFactory(JSON_FACTORY) .setClientSecrets(googleOAuthConfig.clientId, googleOAuthConfig.clientSecret) .build() .setRefreshToken(userGoogleToken.getRefreshToken()); // Thử refresh token - GoogleCredential sẽ tự động refresh nếu cần if (!credential.refreshToken()) { LogHelper.error("Không thể refresh token cho email: " + userGoogleToken.getEmail()); return false; } // Lấy token response từ credential đã refresh String newAccessToken = credential.getAccessToken(); Long expiresInSeconds = credential.getExpiresInSeconds(); // Cập nhật token mới vào database Instant now = Instant.now(); userGoogleToken.setAccessToken(newAccessToken); userGoogleToken.setExpiresIn(expiresInSeconds); userGoogleToken.setUpdatedAt(now); if (expiresInSeconds != null) { userGoogleToken.setExpiresAt(now.plusSeconds(expiresInSeconds)); } userGoogleTokenRepository.save(userGoogleToken); // Tạo TokenResponse để cập nhật vào TokenStore cache TokenResponse tokenResponse = new TokenResponse() .setAccessToken(newAccessToken) .setRefreshToken(userGoogleToken.getRefreshToken()) .setExpiresInSeconds(expiresInSeconds) .setTokenType(userGoogleToken.getTokenType()) .setScope(userGoogleToken.getScope()); if (userGoogleToken.getRefreshTokenExpiresIn() != null) { tokenResponse.set("refresh_token_expires_in", userGoogleToken.getRefreshTokenExpiresIn()); } tokenStore.storeToken(userGoogleToken.getEmail(), tokenResponse); LogHelper.info("Đã refresh thành công access token cho email: " + userGoogleToken.getEmail()); return true; } catch (IOException e) { LogHelper.error("Lỗi khi refresh token cho email: " + userGoogleToken.getEmail() + " - " + e.getMessage()); return false; } catch (Exception e) { LogHelper.error("Lỗi không mong đợi khi refresh token cho email: " + userGoogleToken.getEmail() + " - " + e.getMessage()); return false; } } }