extract Google logic into separate adapter
This commit is contained in:
9
src/main/java/com/ddf/vodsystem/dto/GoogleUser.java
Normal file
9
src/main/java/com/ddf/vodsystem/dto/GoogleUser.java
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package com.ddf.vodsystem.dto;
|
||||||
|
|
||||||
|
public record GoogleUser(
|
||||||
|
String googleId,
|
||||||
|
String email,
|
||||||
|
String name,
|
||||||
|
String profilePictureUrl
|
||||||
|
) {
|
||||||
|
}
|
||||||
@@ -15,9 +15,17 @@ public record AuthProperties (
|
|||||||
@NotBlank @Size(min = 32) String jwtSecret,
|
@NotBlank @Size(min = 32) String jwtSecret,
|
||||||
@Valid @NotNull Expiration expiration
|
@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 (
|
public record Expiration (
|
||||||
@NotNull Duration jwt,
|
@NotNull Duration jwt,
|
||||||
@NotNull Duration refreshToken,
|
@NotNull Duration refreshToken,
|
||||||
@NotNull Duration tokenFamily
|
@NotNull Duration tokenFamily,
|
||||||
|
@NotNull Duration gracePeriod
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.ddf.vodsystem.services;
|
package com.ddf.vodsystem.services;
|
||||||
|
|
||||||
|
import com.ddf.vodsystem.dto.GoogleUser;
|
||||||
import com.ddf.vodsystem.dto.TokenPackage;
|
import com.ddf.vodsystem.dto.TokenPackage;
|
||||||
import com.ddf.vodsystem.dto.properties.AuthProperties;
|
import com.ddf.vodsystem.dto.properties.AuthProperties;
|
||||||
import com.ddf.vodsystem.entities.RefreshToken;
|
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.entities.User;
|
||||||
import com.ddf.vodsystem.exceptions.NotAuthenticated;
|
import com.ddf.vodsystem.exceptions.NotAuthenticated;
|
||||||
import com.ddf.vodsystem.repositories.UserRepository;
|
import com.ddf.vodsystem.repositories.UserRepository;
|
||||||
|
import com.ddf.vodsystem.security.GoogleVerifierService;
|
||||||
import com.ddf.vodsystem.security.JwtService;
|
import com.ddf.vodsystem.security.JwtService;
|
||||||
import com.ddf.vodsystem.security.TokenService;
|
import com.ddf.vodsystem.security.TokenService;
|
||||||
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.GeneralSecurityException;
|
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
@@ -26,7 +24,7 @@ import java.util.Optional;
|
|||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class UserService {
|
public class UserService {
|
||||||
private final GoogleIdTokenVerifier verifier;
|
private final GoogleVerifierService googleVerifierService;
|
||||||
private final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
private final JwtService jwtService;
|
private final JwtService jwtService;
|
||||||
|
|
||||||
@@ -39,11 +37,11 @@ public class UserService {
|
|||||||
|
|
||||||
public UserService(UserRepository userRepository,
|
public UserService(UserRepository userRepository,
|
||||||
JwtService jwtService,
|
JwtService jwtService,
|
||||||
GoogleIdTokenVerifier verifier,
|
GoogleVerifierService googleVerifierService,
|
||||||
AuthProperties props,
|
AuthProperties props,
|
||||||
TokenService tokenService) {
|
TokenService tokenService) {
|
||||||
this.userRepository = userRepository;
|
this.userRepository = userRepository;
|
||||||
this.verifier = verifier;
|
this.googleVerifierService = googleVerifierService;
|
||||||
this.jwtService = jwtService;
|
this.jwtService = jwtService;
|
||||||
this.tokenService = tokenService;
|
this.tokenService = tokenService;
|
||||||
|
|
||||||
@@ -83,15 +81,8 @@ public class UserService {
|
|||||||
*/
|
*/
|
||||||
@Transactional
|
@Transactional
|
||||||
public TokenPackage login(String idToken) {
|
public TokenPackage login(String idToken) {
|
||||||
GoogleIdToken googleIdToken = getGoogleIdToken(idToken);
|
GoogleUser googleUser = googleVerifierService.verify(idToken);
|
||||||
String googleId = googleIdToken.getPayload().getSubject();
|
User user = createOrUpdateGoogleUser(googleUser);
|
||||||
|
|
||||||
if (googleId == null) {
|
|
||||||
throw new NotAuthenticated("Invalid ID token");
|
|
||||||
}
|
|
||||||
|
|
||||||
User googleUser = getGoogleUser(googleIdToken);
|
|
||||||
User user = createOrUpdateUser(googleUser);
|
|
||||||
|
|
||||||
byte[] rawRefresh = generateRandomBytes(32);
|
byte[] rawRefresh = generateRandomBytes(32);
|
||||||
TokenFamily tokenFamily = tokenService.createTokenFamily(user, tokenFamilyExpirationMs);
|
TokenFamily tokenFamily = tokenService.createTokenFamily(user, tokenFamilyExpirationMs);
|
||||||
@@ -161,46 +152,25 @@ public class UserService {
|
|||||||
return userRepository.findByStreamKey(streamKey);
|
return userRepository.findByStreamKey(streamKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
private User createOrUpdateUser(User user) {
|
private User createOrUpdateGoogleUser(GoogleUser googleUser) {
|
||||||
Optional<User> existingUser = userRepository.findByGoogleId(user.getGoogleId());
|
Optional<User> existingUser = userRepository.findByGoogleId(googleUser.googleId());
|
||||||
|
User user;
|
||||||
|
|
||||||
if (existingUser.isEmpty()) {
|
if (existingUser.isEmpty()) {
|
||||||
|
user = new User();
|
||||||
|
user.setGoogleId(googleUser.googleId());
|
||||||
|
user.setUsername(googleUser.email());
|
||||||
user.setRole(0);
|
user.setRole(0);
|
||||||
user.setCreatedAt(Instant.now());
|
user.setCreatedAt(Instant.now());
|
||||||
user.setStreamKey(HexFormat.of().formatHex(generateRandomBytes(24)));
|
user.setStreamKey(HexFormat.of().formatHex(generateRandomBytes(24)));
|
||||||
return userRepository.saveAndFlush(user);
|
} else {
|
||||||
|
user = existingUser.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
User existing = existingUser.get();
|
user.setEmail(googleUser.email());
|
||||||
existing.setEmail(user.getEmail());
|
user.setName(googleUser.name());
|
||||||
existing.setName(user.getName());
|
user.setProfilePictureUrl(googleUser.profilePictureUrl());
|
||||||
existing.setProfilePictureUrl(user.getProfilePictureUrl());
|
return userRepository.saveAndFlush(user);
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] generateRandomBytes(int length) {
|
private byte[] generateRandomBytes(int length) {
|
||||||
|
|||||||
@@ -28,6 +28,9 @@ app.security.expiration.refresh-token=604800000
|
|||||||
# 90 days
|
# 90 days
|
||||||
app.security.expiration.token-family=7776000000
|
app.security.expiration.token-family=7776000000
|
||||||
|
|
||||||
|
# 5 seconds
|
||||||
|
app.security.expiration.grace-period=5000
|
||||||
|
|
||||||
# Frontend
|
# Frontend
|
||||||
frontend.url=${FRONTEND_URL}
|
frontend.url=${FRONTEND_URL}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user