-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathGoogleOAuthStrategy.java
More file actions
142 lines (122 loc) · 7.06 KB
/
Copy pathGoogleOAuthStrategy.java
File metadata and controls
142 lines (122 loc) · 7.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package com.danielagapov.spawn.auth.internal.services;
import com.danielagapov.spawn.shared.util.OAuthProvider;
import com.danielagapov.spawn.shared.exceptions.Logger.ILogger;
import com.danielagapov.spawn.shared.exceptions.TokenExpiredException;
import com.danielagapov.spawn.shared.exceptions.OAuthProviderUnavailableException;
import com.danielagapov.spawn.shared.util.RetryHelper;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.gson.GsonFactory;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Collections;
@Service
public final class GoogleOAuthStrategy implements OAuthStrategy {
private final ILogger logger;
private GoogleIdTokenVerifier verifier;
@Value("${google.client.id}")
private String googleClientId;
@Autowired
public GoogleOAuthStrategy(ILogger logger) {
this.logger = logger;
// Don't initialize verifier here - will be initialized in @PostConstruct with proper client ID
}
@Override
public OAuthProvider getOAuthProvider() {
return OAuthProvider.google;
}
/**
* Verifies a Google ID token and extracts the subject (user ID)
*
* @param idToken Google ID token to verify
* @return the subject (user ID) extracted from the token
*/
@Override
public String verifyIdToken(String idToken) {
try {
logger.info("Attempting to verify Google ID token");
logger.info("Using client ID: " + googleClientId);
// Use retry helper for token verification
return RetryHelper.executeOAuthWithRetry(() -> {
try {
GoogleIdToken googleIdToken = null;
// Verify the token
try {
googleIdToken = verifier.verify(idToken);
} catch (Error e) {
logger.error(e.getMessage());
}
if (googleIdToken == null) {
logger.error("Token verification failed - invalid token. Token prefix: " + (idToken != null ? idToken.substring(0, Math.min(20, idToken.length())) + "..." : "null"));
throw new SecurityException("Invalid Google ID token - token may be expired or malformed");
}
logger.info("Token verified successfully");
// Get payload data
GoogleIdToken.Payload payload = googleIdToken.getPayload();
String userId = payload.getSubject(); // Get the user's ID
logger.info("Extracted user ID: " + userId);
// Check token expiration
Long expiration = payload.getExpirationTimeSeconds();
if (expiration != null && expiration < System.currentTimeMillis() / 1000) {
logger.error("Token has expired. Expiration: " + expiration + ", Current time: " + (System.currentTimeMillis() / 1000));
throw new TokenExpiredException("Google ID token has expired, please sign in again");
}
// Verify additional claims if needed
// For example, verify email is verified
Boolean emailVerified = payload.getEmailVerified();
if (emailVerified == null || !emailVerified) {
logger.error("Email not verified for user ID: " + userId + ", emailVerified value: " + emailVerified);
throw new SecurityException("Google account email is not verified");
}
return userId;
} catch (GeneralSecurityException e) {
logger.error("Security error during token verification: " + e.getMessage());
throw new SecurityException("Security error during Google token verification: " + e.getMessage(), e);
} catch (IOException e) {
logger.error("Network error during token verification: " + e.getMessage());
throw new OAuthProviderUnavailableException("Google authentication service is temporarily unavailable. Please try again later.", e);
}
});
} catch (TokenExpiredException e) {
logger.error("Token expired: " + e.getMessage());
throw e;
} catch (OAuthProviderUnavailableException e) {
logger.error("OAuth provider unavailable: " + e.getMessage());
throw e;
} catch (Exception e) {
logger.error("Unexpected error during token verification: " + e.getMessage());
logger.error("Token details: " + (idToken != null ? idToken.substring(0, Math.min(20, idToken.length())) + "..." : "null"));
throw new SecurityException("Unexpected error during Google token verification: " + e.getMessage(), e);
}
}
// Updated method with @PostConstruct to ensure client ID is loaded from properties
@PostConstruct
public void initializeGoogleVerifier() {
// Try to get client ID from property, which should come from env variable
String clientId = googleClientId;
logger.info("Retrieved Google client ID from application properties: " + (clientId != null ? (clientId.substring(0, Math.min(10, clientId.length())) + "...") : "null"));
// If not set in property, try to get directly from environment
if (clientId == null || clientId.isEmpty()) {
clientId = System.getenv("GOOGLE_CLIENT_ID");
logger.info("Getting Google client ID directly from environment variable: " + (clientId != null ? (clientId.substring(0, Math.min(10, clientId.length())) + "...") : "null"));
}
// Re-initialize Google ID token verifier with client ID from properties or environment
if (clientId != null && !clientId.isEmpty()) {
logger.info("Initializing Google token verifier with client ID: " + clientId);
this.verifier = new GoogleIdTokenVerifier.Builder(new NetHttpTransport(), new GsonFactory())
.setAudience(Collections.singletonList(clientId))
.build();
logger.info("Google token verifier successfully initialized");
} else {
logger.error("Google client ID not set, token verification will fail. Set GOOGLE_CLIENT_ID in your environment. clientId value: " + (clientId == null ? "null" : "empty string"));
// Create a dummy verifier that will reject all tokens
this.verifier = new GoogleIdTokenVerifier.Builder(new NetHttpTransport(), new GsonFactory()).build();
logger.warn("Created dummy verifier that will reject all tokens - Google OAuth will not work");
}
}
}