From cbb8c2195f77705acae7d5454375a6661bf3e0c5 Mon Sep 17 00:00:00 2001 From: Dylan De Faoite Date: Sun, 4 Oct 2026 14:54:23 +0100 Subject: [PATCH] extract Google logic into separate adapter --- .../com/ddf/vodsystem/dto/GoogleUser.java | 9 +++ .../dto/properties/AuthProperties.java | 10 ++- .../security/GoogleVerifierService.java | 41 +++++++++++ .../ddf/vodsystem/services/UserService.java | 68 ++++++------------- src/main/resources/application.properties | 3 + 5 files changed, 81 insertions(+), 50 deletions(-) create mode 100644 src/main/java/com/ddf/vodsystem/dto/GoogleUser.java create mode 100644 src/main/java/com/ddf/vodsystem/security/GoogleVerifierService.java diff --git a/src/main/java/com/ddf/vodsystem/dto/GoogleUser.java b/src/main/java/com/ddf/vodsystem/dto/GoogleUser.java new file mode 100644 index 0000000..68beaa7 --- /dev/null +++ b/src/main/java/com/ddf/vodsystem/dto/GoogleUser.java @@ -0,0 +1,9 @@ +package com.ddf.vodsystem.dto; + +public record GoogleUser( + String googleId, + String email, + String name, + String profilePictureUrl +) { +} diff --git a/src/main/java/com/ddf/vodsystem/dto/properties/AuthProperties.java b/src/main/java/com/ddf/vodsystem/dto/properties/AuthProperties.java index ea4c3af..194b40f 100644 --- a/src/main/java/com/ddf/vodsystem/dto/properties/AuthProperties.java +++ b/src/main/java/com/ddf/vodsystem/dto/properties/AuthProperties.java @@ -15,9 +15,17 @@ public record AuthProperties ( @NotBlank @Size(min = 32) String jwtSecret, @Valid @NotNull Expiration expiration ) { + + /** + * + * @param jwt Duration object as to the length of the access token + * @param refreshToken Duration of refresh token + * @param tokenFamily Duration of token family + */ public record Expiration ( @NotNull Duration jwt, @NotNull Duration refreshToken, - @NotNull Duration tokenFamily + @NotNull Duration tokenFamily, + @NotNull Duration gracePeriod ) {} } diff --git a/src/main/java/com/ddf/vodsystem/security/GoogleVerifierService.java b/src/main/java/com/ddf/vodsystem/security/GoogleVerifierService.java new file mode 100644 index 0000000..e3497db --- /dev/null +++ b/src/main/java/com/ddf/vodsystem/security/GoogleVerifierService.java @@ -0,0 +1,41 @@ +package com.ddf.vodsystem.security; + +import com.ddf.vodsystem.dto.GoogleUser; +import com.ddf.vodsystem.exceptions.NotAuthenticated; +import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken; +import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.security.GeneralSecurityException; + +@Service +public class GoogleVerifierService { + private final GoogleIdTokenVerifier verifier; + + public GoogleVerifierService(GoogleIdTokenVerifier verifier) { + this.verifier = verifier; + } + + public GoogleUser verify(String idToken) { + GoogleIdToken token; + try { + token = verifier.verify(idToken); + } catch (GeneralSecurityException | IOException e) { + throw new NotAuthenticated("Invalid ID token: " + e.getMessage()); + } + + String googleId = token.getPayload().getSubject(); + + if (googleId == null) { + throw new NotAuthenticated("Google ID does not exist"); + } + + return new GoogleUser( + googleId, + token.getPayload().getEmail(), + (String) token.getPayload().get("name"), + (String) token.getPayload().get("picture") + ); + } +} diff --git a/src/main/java/com/ddf/vodsystem/services/UserService.java b/src/main/java/com/ddf/vodsystem/services/UserService.java index d53ab70..6dcb332 100644 --- a/src/main/java/com/ddf/vodsystem/services/UserService.java +++ b/src/main/java/com/ddf/vodsystem/services/UserService.java @@ -1,5 +1,6 @@ package com.ddf.vodsystem.services; +import com.ddf.vodsystem.dto.GoogleUser; import com.ddf.vodsystem.dto.TokenPackage; import com.ddf.vodsystem.dto.properties.AuthProperties; import com.ddf.vodsystem.entities.RefreshToken; @@ -7,17 +8,14 @@ import com.ddf.vodsystem.entities.TokenFamily; import com.ddf.vodsystem.entities.User; import com.ddf.vodsystem.exceptions.NotAuthenticated; import com.ddf.vodsystem.repositories.UserRepository; +import com.ddf.vodsystem.security.GoogleVerifierService; import com.ddf.vodsystem.security.JwtService; import com.ddf.vodsystem.security.TokenService; -import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken; import org.springframework.transaction.annotation.Transactional; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; -import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier; import org.springframework.stereotype.Service; -import java.io.IOException; -import java.security.GeneralSecurityException; import java.security.SecureRandom; import java.time.Duration; import java.time.Instant; @@ -26,7 +24,7 @@ import java.util.Optional; @Service public class UserService { - private final GoogleIdTokenVerifier verifier; + private final GoogleVerifierService googleVerifierService; private final UserRepository userRepository; private final JwtService jwtService; @@ -39,11 +37,11 @@ public class UserService { public UserService(UserRepository userRepository, JwtService jwtService, - GoogleIdTokenVerifier verifier, + GoogleVerifierService googleVerifierService, AuthProperties props, TokenService tokenService) { this.userRepository = userRepository; - this.verifier = verifier; + this.googleVerifierService = googleVerifierService; this.jwtService = jwtService; this.tokenService = tokenService; @@ -83,15 +81,8 @@ public class UserService { */ @Transactional public TokenPackage login(String idToken) { - GoogleIdToken googleIdToken = getGoogleIdToken(idToken); - String googleId = googleIdToken.getPayload().getSubject(); - - if (googleId == null) { - throw new NotAuthenticated("Invalid ID token"); - } - - User googleUser = getGoogleUser(googleIdToken); - User user = createOrUpdateUser(googleUser); + GoogleUser googleUser = googleVerifierService.verify(idToken); + User user = createOrUpdateGoogleUser(googleUser); byte[] rawRefresh = generateRandomBytes(32); TokenFamily tokenFamily = tokenService.createTokenFamily(user, tokenFamilyExpirationMs); @@ -161,46 +152,25 @@ public class UserService { return userRepository.findByStreamKey(streamKey); } - private User createOrUpdateUser(User user) { - Optional existingUser = userRepository.findByGoogleId(user.getGoogleId()); + private User createOrUpdateGoogleUser(GoogleUser googleUser) { + Optional existingUser = userRepository.findByGoogleId(googleUser.googleId()); + User user; if (existingUser.isEmpty()) { + user = new User(); + user.setGoogleId(googleUser.googleId()); + user.setUsername(googleUser.email()); user.setRole(0); user.setCreatedAt(Instant.now()); user.setStreamKey(HexFormat.of().formatHex(generateRandomBytes(24))); - return userRepository.saveAndFlush(user); + } else { + user = existingUser.get(); } - User existing = existingUser.get(); - existing.setEmail(user.getEmail()); - existing.setName(user.getName()); - existing.setProfilePictureUrl(user.getProfilePictureUrl()); - existing.setUsername(user.getUsername()); - return userRepository.saveAndFlush(existing); - } - - private User getGoogleUser(GoogleIdToken idToken) { - String googleId = idToken.getPayload().getSubject(); - String email = idToken.getPayload().getEmail(); - String name = (String) idToken.getPayload().get("name"); - String profilePictureUrl = (String) idToken.getPayload().get("picture"); - - User user = new User(); - user.setGoogleId(googleId); - user.setEmail(email); - user.setName(name); - user.setUsername(email); - user.setProfilePictureUrl(profilePictureUrl); - - return user; - } - - private GoogleIdToken getGoogleIdToken(String idToken) { - try { - return verifier.verify(idToken); - } catch (GeneralSecurityException | IOException e) { - throw new NotAuthenticated("Invalid ID token: " + e.getMessage()); - } + user.setEmail(googleUser.email()); + user.setName(googleUser.name()); + user.setProfilePictureUrl(googleUser.profilePictureUrl()); + return userRepository.saveAndFlush(user); } private byte[] generateRandomBytes(int length) { diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 380feb4..d881404 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -28,6 +28,9 @@ app.security.expiration.refresh-token=604800000 # 90 days app.security.expiration.token-family=7776000000 +# 5 seconds +app.security.expiration.grace-period=5000 + # Frontend frontend.url=${FRONTEND_URL}