1313import com .incial .stockflow .repository .UserRepository ;
1414import com .incial .stockflow .security .JwtUtil ;
1515import lombok .RequiredArgsConstructor ;
16+ import lombok .extern .slf4j .Slf4j ;
1617import org .springframework .beans .factory .annotation .Value ;
17- import org .springframework .security .core .userdetails .UsernameNotFoundException ;
18-
1918import org .springframework .security .crypto .password .PasswordEncoder ;
2019import org .springframework .stereotype .Service ;
2120import org .springframework .transaction .annotation .Transactional ;
2423import java .security .GeneralSecurityException ;
2524import java .util .Collections ;
2625
26+ @ Slf4j
2727@ Service
2828@ RequiredArgsConstructor
2929public class AuthService {
30-
30+
3131 private final UserRepository userRepository ;
3232 private final PasswordEncoder passwordEncoder ;
3333 private final JwtUtil tokenProvider ;
3434 private final AuditService auditService ;
3535
3636 @ Value ("${google.client.id}" )
3737 private String googleClientId ;
38-
38+
3939 @ Transactional
4040 public LoginResponse login (LoginRequest request ) {
4141 User user = userRepository .findByEmail (request .getEmail ())
4242 .orElseThrow (() -> new UnauthorizedException ("AUTH_001" , "Invalid email or password" ));
43-
43+
4444 if (!passwordEncoder .matches (request .getPassword (), user .getPassword ())) {
4545 throw new UnauthorizedException ("AUTH_001" , "Invalid email or password" );
4646 }
47-
47+
4848 String token = tokenProvider .generateToken (user .getId (),user .getEmail (), String .valueOf (user .getRole ()));
49-
49+
5050 // Log the login
5151 auditService .logAction (user , "LOGIN" , "User" , user .getId (), "User logged in successfully" );
52-
52+
5353 UserResponse userResponse = UserResponse .builder ()
5454 .id (user .getId ())
5555 .name (user .getName ())
@@ -58,105 +58,110 @@ public LoginResponse login(LoginRequest request) {
5858 .outletId (user .getOutlet () != null ? user .getOutlet ().getId () : null )
5959 .avatarUrl (user .getAvatarUrl ())
6060 .build ();
61-
61+
6262 return LoginResponse .builder ()
6363 .token (token )
6464 .user (userResponse )
6565 .build ();
6666 }
6767
68- @ Transactional
6968 public LoginResponse loginWithGoogle (GoogleLoginRequest request ) {
70-
71- if (request == null || request .getCredential () == null || request .getCredential ().isBlank ()) {
72- throw new UnauthorizedException ("AUTH_GOOGLE_001" , "Missing Google credential" );
73- }
74-
75- if (googleClientId == null || googleClientId .trim ().isEmpty ()) {
76- throw new IllegalStateException ("Google authentication not configured on server" );
77- }
78-
79- GoogleIdToken idToken ;
80-
69+ log .info ("Google login attempt starting" );
8170 try {
71+ // Check if Google Client ID is configured
72+ if (googleClientId == null || googleClientId .trim ().isEmpty ()) {
73+ log .error ("Google Client ID not configured" );
74+ throw new UnauthorizedException ("AUTH_003" , "Google authentication is not properly configured. Please contact the administrator." );
75+ }
76+
77+ log .debug ("Creating Google ID token verifier" );
78+ // Verify Google ID token with timeout protection
8279 GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier .Builder (
8380 new NetHttpTransport (),
8481 GsonFactory .getDefaultInstance ())
8582 .setAudience (Collections .singletonList (googleClientId ))
8683 .build ();
8784
88- idToken = verifier .verify (request .getCredential ());
89-
90- } catch (Exception e ) {
91- throw new UnauthorizedException ("AUTH_GOOGLE_002" , "Invalid Google token" );
92- }
93-
94- if (idToken == null ) {
95- throw new UnauthorizedException ("AUTH_GOOGLE_003" , "Invalid Google token" );
96- }
97-
98- GoogleIdToken .Payload payload ;
99- try {
100- payload = idToken .getPayload ();
101- } catch (Exception e ) {
102- throw new UnauthorizedException ("AUTH_GOOGLE_004" , "Invalid Google payload" );
103- }
104-
105- String email = payload .getEmail ();
106- String googleId = payload .getSubject ();
107-
108- if (email == null || googleId == null ) {
109- throw new UnauthorizedException ("AUTH_GOOGLE_005" , "Google account missing required data" );
110- }
111-
112- String name = (String ) payload .get ("name" );
113- String pictureUrl = (String ) payload .get ("picture" );
114-
115- // User lookup
116- User user = userRepository .findByEmail (email )
117- .orElseThrow (() ->
118- new UnauthorizedException ("AUTH_GOOGLE_006" ,
119- "User not associated with this email. Contact administrator" ));
120-
121- // Update Google details if changed
122- boolean needsUpdate = false ;
123-
124- if (user .getGoogleId () == null || !user .getGoogleId ().equals (googleId )) {
125- user .setGoogleId (googleId );
126- needsUpdate = true ;
127- }
85+ GoogleIdToken idToken = null ;
86+ try {
87+ log .debug ("Verifying Google token - this may involve network I/O to googleapis.com" );
88+ long startTime = System .currentTimeMillis ();
89+ idToken = verifier .verify (request .getCredential ());
90+ long duration = System .currentTimeMillis () - startTime ;
91+ log .info ("Google token verification completed in {}ms" , duration );
92+ } catch (java .net .SocketTimeoutException e ) {
93+ log .error ("Socket timeout during Google token verification" , e );
94+ throw new RuntimeException ("Network timeout while verifying Google token. Please check your internet connection and try again." );
95+ } catch (java .net .UnknownHostException e ) {
96+ log .error ("Unknown host during Google token verification - cannot reach googleapis.com" , e );
97+ throw new RuntimeException ("Cannot reach Google authentication servers. Please check network connectivity." );
98+ } catch (javax .net .ssl .SSLException e ) {
99+ log .error ("SSL error during Google token verification" , e );
100+ throw new RuntimeException ("SSL certificate error during Google authentication. Please contact system administrator." );
101+ } catch (IOException e ) {
102+ log .error ("IO error during Google token verification" , e );
103+ throw new RuntimeException ("Network error during Google authentication: " + e .getMessage ());
104+ }
105+
106+ if (idToken == null ) {
107+ log .warn ("Google token verification returned null - invalid token" );
108+ throw new UnauthorizedException ("AUTH_004" , "Invalid Google ID token. Please try logging in again." );
109+ }
110+
111+ GoogleIdToken .Payload payload = idToken .getPayload ();
112+ String googleId = payload .getSubject ();
113+ String email = payload .getEmail ();
114+ String name = (String ) payload .get ("name" );
115+ String pictureUrl = (String ) payload .get ("picture" );
116+
117+ log .info ("Google token verified successfully for email: {}" , email );
118+
119+ // Find user by email - user must be pre-registered
120+ User user = userRepository .findByEmail (email ).orElseThrow (
121+ () -> {
122+ log .warn ("Google login attempted for unregistered email: {}" , email );
123+ return new UnauthorizedException ("AUTH_005" , "User not associated with this email. Contact sales" );
124+ }
125+ );
126+
127+ // Update existing user with Google info only if values changed
128+ boolean needsUpdate = false ;
129+ if (user .getGoogleId () == null || !user .getGoogleId ().equals (googleId )) {
130+ user .setGoogleId (googleId );
131+ needsUpdate = true ;
132+ }
133+ if (pictureUrl != null && (user .getAvatarUrl () == null || !user .getAvatarUrl ().equals (pictureUrl ))) {
134+ user .setAvatarUrl (pictureUrl );
135+ needsUpdate = true ;
136+ }
137+ if (needsUpdate ) {
138+ userRepository .save (user );
139+ log .debug ("Updated user Google info for: {}" , email );
140+ }
141+
142+ String token = tokenProvider .generateToken (user .getId (),user .getEmail (), String .valueOf (user .getRole ()));
143+
144+ // Log the login
145+ auditService .logAction (user , "GOOGLE LOGIN" , "User" , user .getId (), "User logged in successfully" );
146+
147+ UserResponse userResponse = UserResponse .builder ()
148+ .id (user .getId ())
149+ .name (user .getName ())
150+ .email (user .getEmail ())
151+ .role (user .getRole ())
152+ .outletId (user .getOutlet () != null ? user .getOutlet ().getId () : null )
153+ .avatarUrl (user .getAvatarUrl ())
154+ .build ();
128155
129- if (pictureUrl != null && (user .getAvatarUrl () == null || !user .getAvatarUrl ().equals (pictureUrl ))) {
130- user .setAvatarUrl (pictureUrl );
131- needsUpdate = true ;
132- }
156+ log .info ("Google login successful for user: {}" , email );
157+ return LoginResponse .builder ()
158+ .token (token )
159+ .user (userResponse )
160+ .build ();
133161
134- if (needsUpdate ) {
135- userRepository .save (user );
162+ } catch (GeneralSecurityException e ) {
163+ log .error ("Security exception during Google token verification" , e );
164+ throw new RuntimeException ("Google token verification failed: " + e .getMessage ());
136165 }
137-
138- // Generate JWT
139- String token = tokenProvider .generateToken (
140- user .getId (),
141- user .getEmail (),
142- String .valueOf (user .getRole ())
143- );
144-
145- auditService .logAction (user , "GOOGLE LOGIN" , "User" , user .getId (), "User logged in successfully" );
146-
147- UserResponse userResponse = UserResponse .builder ()
148- .id (user .getId ())
149- .name (user .getName ())
150- .email (user .getEmail ())
151- .role (user .getRole ())
152- .outletId (user .getOutlet () != null ? user .getOutlet ().getId () : null )
153- .avatarUrl (user .getAvatarUrl ())
154- .build ();
155-
156- return LoginResponse .builder ()
157- .token (token )
158- .user (userResponse )
159- .build ();
160166 }
161-
162167}
0 commit comments