Skip to content

Commit b0c5f10

Browse files
committed
fix: Update OAuth implementation 7
1 parent 258fa67 commit b0c5f10

File tree

3 files changed

+22
-59
lines changed

3 files changed

+22
-59
lines changed

src/main/java/com/contentstack/cms/models/OAuthConfig.java

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ public void validate() {
3838
throw new IllegalArgumentException("redirectUri is required");
3939
}
4040

41-
// Validate redirectUri is a valid URL
4241
try {
4342
new URL(redirectUri);
4443
} catch (MalformedURLException e) {
@@ -63,10 +62,8 @@ public String getFormattedAuthorizationEndpoint() {
6362
return authEndpoint;
6463
}
6564

66-
// Transform hostname similar to JS SDK
6765
String hostname = "app.contentstack.com";
6866

69-
// Handle environment-specific transformations
7067
if (hostname.endsWith("io")) {
7168
hostname = hostname.replace("io", "com");
7269
}
@@ -86,12 +83,9 @@ public String getTokenEndpoint() {
8683
return tokenEndpoint;
8784
}
8885

89-
// Transform for developer hub
9086
String hostname = "developerhub-api.contentstack.com";
91-
92-
// Handle environment-specific transformations
9387
hostname = hostname
94-
.replaceAll("^dev\\d+", "dev") // Replace dev1, dev2, etc. with dev
88+
.replaceAll("^dev\\d+", "dev")
9589
.replace("io", "com");
9690

9791
return "https://" + hostname + "/token";

src/main/java/com/contentstack/cms/oauth/OAuthHandler.java

Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
*/
2929
@Getter
3030
public class OAuthHandler {
31-
private final OkHttpClient httpClient;
31+
private OkHttpClient httpClient;
3232
private final OAuthConfig config;
3333
private final Gson gson;
3434

@@ -55,14 +55,11 @@ public OAuthHandler(OkHttpClient httpClient, OAuthConfig config) {
5555
// Only generate PKCE codeVerifier if clientSecret is not provided
5656
if (config.getClientSecret() == null || config.getClientSecret().trim().isEmpty()) {
5757
this.codeVerifier = generateCodeVerifier();
58-
// codeChallenge will be generated during authorize()
5958
this.codeChallenge = null;
6059
}
6160
}
6261

63-
/**
64-
* Returns common headers for OAuth requests
65-
*/
62+
6663
private Request.Builder _getHeaders() {
6764
return new Request.Builder()
6865
.header("Content-Type", "application/x-www-form-urlencoded")
@@ -74,10 +71,11 @@ private Request.Builder _getHeaders() {
7471
* @return A random URL-safe string between 43-128 characters
7572
*/
7673
private String generateCodeVerifier() {
74+
final int CODE_VERIFIER_LENGTH = 96;
7775
final String charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
7876
SecureRandom random = new SecureRandom();
7977
StringBuilder verifier = new StringBuilder();
80-
for (int i = 0; i < 128; i++) {
78+
for (int i = 0; i < CODE_VERIFIER_LENGTH; i++) {
8179
verifier.append(charset.charAt(random.nextInt(charset.length())));
8280
}
8381
return verifier.toString();
@@ -103,14 +101,6 @@ private String generateCodeChallenge(String verifier) {
103101
}
104102
}
105103

106-
/**
107-
* Generates PKCE parameters (code verifier and challenge)
108-
*/
109-
private void generatePkceParameters() {
110-
this.codeVerifier = generateCodeVerifier();
111-
this.codeChallenge = generateCodeChallenge(this.codeVerifier);
112-
113-
}
114104

115105
/**
116106
* Starts the OAuth authorization flow
@@ -124,6 +114,11 @@ public String authorize() {
124114
urlBuilder.append("?response_type=").append(config.getResponseType())
125115
.append("&client_id=").append(URLEncoder.encode(config.getClientId(), "UTF-8"))
126116
.append("&redirect_uri=").append(URLEncoder.encode(config.getRedirectUri(), "UTF-8"));
117+
118+
// Add scope if provided
119+
if (config.getScope() != null && !config.getScope().trim().isEmpty()) {
120+
urlBuilder.append("&scope=").append(URLEncoder.encode(config.getScope(), "UTF-8"));
121+
}
127122

128123
if (config.getClientSecret() != null && !config.getClientSecret().trim().isEmpty()) {
129124
return urlBuilder.toString();
@@ -179,17 +174,6 @@ public CompletableFuture<OAuthTokens> exchangeCodeForToken(String code) {
179174
*/
180175
private void _saveTokens(OAuthTokens tokens) {
181176
this.tokens = tokens;
182-
// Update the client's auth state
183-
if (tokens != null && tokens.hasAccessToken()) {
184-
this.httpClient = this.httpClient.newBuilder()
185-
.addInterceptor(chain -> {
186-
Request request = chain.request().newBuilder()
187-
.header("Authorization", "Bearer " + tokens.getAccessToken())
188-
.build();
189-
return chain.proceed(request);
190-
})
191-
.build();
192-
}
193177
}
194178

195179
/**
@@ -248,7 +232,14 @@ private OAuthTokens executeTokenRequest(Request request) throws IOException {
248232
if (!response.isSuccessful()) {
249233
String error = responseBody != null ? responseBody.string() : "Unknown error";
250234
System.err.println("Error Response Body: " + error);
251-
throw new RuntimeException("Token request failed with status " + response.code() + ": " + error);
235+
236+
// Parse error response if possible
237+
try {
238+
throw new RuntimeException("Token request failed: " + error);
239+
} catch (JsonParseException e) {
240+
throw new RuntimeException("Token request failed with status " +
241+
response.code() + ": " + error);
242+
}
252243
}
253244

254245
String body = responseBody != null ? responseBody.string() : "{}";

src/main/java/com/contentstack/cms/oauth/OAuthInterceptor.java

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@
1111
import java.util.concurrent.TimeoutException;
1212
import java.util.logging.Logger;
1313

14-
/**
15-
* OkHttp interceptor that handles OAuth token injection and refresh
16-
*/
14+
1715
public class OAuthInterceptor implements Interceptor {
1816
private static final Logger logger = Logger.getLogger(OAuthInterceptor.class.getName());
1917
private final OAuthHandler oauthHandler;
@@ -23,26 +21,17 @@ public OAuthInterceptor(OAuthHandler oauthHandler) {
2321
this.oauthHandler = oauthHandler;
2422
}
2523

26-
/**
27-
* Sets early access features
28-
* @param earlyAccess Array of early access feature names
29-
*/
24+
3025
public void setEarlyAccess(String[] earlyAccess) {
3126
this.earlyAccess = earlyAccess;
3227
}
3328

34-
/**
35-
* Checks if OAuth is configured
36-
* @return true if OAuth is configured
37-
*/
29+
3830
public boolean isOAuthConfigured() {
3931
return oauthHandler != null && oauthHandler.getConfig() != null;
4032
}
4133

42-
/**
43-
* Checks if we have valid tokens
44-
* @return true if we have valid tokens
45-
*/
34+
4635
public boolean hasValidTokens() {
4736
return oauthHandler != null &&
4837
oauthHandler.getTokens() != null &&
@@ -52,41 +41,30 @@ public boolean hasValidTokens() {
5241
@Override
5342
public Response intercept(Chain chain) throws IOException {
5443
Request originalRequest = chain.request();
55-
56-
// Add standard headers
5744
Request.Builder requestBuilder = originalRequest.newBuilder()
5845
.header("X-User-Agent", Util.defaultUserAgent())
5946
.header("User-Agent", Util.defaultUserAgent())
6047
.header("Content-Type", originalRequest.url().toString().contains("/token") ? "application/x-www-form-urlencoded" : "application/json")
6148
.header("X-Header-EA", earlyAccess != null ? String.join(",", earlyAccess) : "true");
62-
63-
// Get current tokens
6449
if (oauthHandler.getTokens() != null) {
65-
// Check if token is expired and refresh if needed
6650
if (oauthHandler.getTokens().isExpired() && oauthHandler.getTokens().hasRefreshToken()) {
6751
try {
6852
oauthHandler.refreshAccessToken().get(30, TimeUnit.SECONDS);
6953
} catch (InterruptedException | ExecutionException | TimeoutException e) {
7054
throw new IOException("Failed to refresh access token", e);
7155
}
7256
}
73-
74-
// Add token to request if available
7557
if (oauthHandler.getTokens().hasAccessToken()) {
7658
requestBuilder.header("Authorization", "Bearer " + oauthHandler.getAccessToken());
7759
}
7860
}
7961

8062
Request request = requestBuilder.build();
8163
Response response = chain.proceed(request);
82-
83-
// Handle 401 by refreshing token and retrying once
8464
if (response.code() == 401 && oauthHandler.getTokens() != null && oauthHandler.getTokens().hasRefreshToken()) {
8565
response.close();
8666
try {
8767
oauthHandler.refreshAccessToken().get(30, TimeUnit.SECONDS);
88-
89-
// Retry with new token
9068
request = request.newBuilder()
9169
.header("Authorization", "Bearer " + oauthHandler.getAccessToken())
9270
.build();

0 commit comments

Comments
 (0)