From 87317f0415ce7e1e53107fdf9578ca0db64b90db Mon Sep 17 00:00:00 2001 From: reeshika-h Date: Mon, 15 Sep 2025 08:20:56 +0530 Subject: [PATCH 1/2] refactor: Simplify OAuth configuration methods and enhance PKCE flow support - Removed deprecated setOAuthWithPKCE methods and consolidated OAuth configuration into a single setOAuth method. - Updated documentation to clarify clientSecret usage for PKCE flow. - Adjusted error message for missing OAuth configuration to reflect new method usage. --- .../com/contentstack/cms/Contentstack.java | 56 ++++--------------- .../java/com/contentstack/cms/core/Util.java | 2 +- .../com/contentstack/cms/oauth/OAuthTest.java | 6 +- 3 files changed, 16 insertions(+), 48 deletions(-) diff --git a/src/main/java/com/contentstack/cms/Contentstack.java b/src/main/java/com/contentstack/cms/Contentstack.java index a98c6e7..c42f357 100644 --- a/src/main/java/com/contentstack/cms/Contentstack.java +++ b/src/main/java/com/contentstack/cms/Contentstack.java @@ -740,14 +740,6 @@ public Builder setOAuthConfig(OAuthConfig config) { return this; } - /** - * Configures OAuth with client credentials (traditional flow) - * @param appId Application ID - * @param clientId Client ID - * @param clientSecret Client secret - * @param redirectUri Redirect URI - * @return Builder instance - */ private TokenCallback tokenCallback; /** @@ -760,64 +752,40 @@ public Builder setTokenCallback(TokenCallback callback) { return this; } - public Builder setOAuth(String appId, String clientId, String clientSecret, String redirectUri) { - // Use the builder's hostname (which defaults to Util.HOST if not set) - return setOAuth(appId, clientId, clientSecret, redirectUri, this.hostname); - } - /** - * Configures OAuth with client credentials and specific host + * Configures OAuth authentication. PKCE flow is automatically used when clientSecret is null or empty. * @param appId Application ID * @param clientId Client ID - * @param clientSecret Client secret + * @param clientSecret Client secret (optional). If null or empty, PKCE flow will be used * @param redirectUri Redirect URI - * @param host API host (e.g. "api.contentstack.io", "eu-api.contentstack.com") * @return Builder instance */ - public Builder setOAuth(String appId, String clientId, String clientSecret, String redirectUri, String host) { - OAuthConfig.OAuthConfigBuilder builder = OAuthConfig.builder() - .appId(appId) - .clientId(clientId) - .clientSecret(clientSecret) - .redirectUri(redirectUri) - .host(host); - - // Add token callback if set - if (this.tokenCallback != null) { - builder.tokenCallback(this.tokenCallback); - } - - this.oauthConfig = builder.build(); - return this; - } - - /** - * Configures OAuth with PKCE (no client secret) - * @param appId Application ID - * @param clientId Client ID - * @param redirectUri Redirect URI - * @return Builder instance - */ - public Builder setOAuthWithPKCE(String appId, String clientId, String redirectUri) { + public Builder setOAuth(String appId, String clientId, String clientSecret, String redirectUri) { // Use the builder's hostname (which defaults to Util.HOST if not set) - return setOAuthWithPKCE(appId, clientId, redirectUri, this.hostname); + return setOAuth(appId, clientId, clientSecret, redirectUri, this.hostname); } /** - * Configures OAuth with PKCE (no client secret) and specific host + * Configures OAuth authentication with a specific host. PKCE flow is automatically used when clientSecret is null or empty. * @param appId Application ID * @param clientId Client ID + * @param clientSecret Client secret (optional). If null or empty, PKCE flow will be used * @param redirectUri Redirect URI * @param host API host (e.g. "api.contentstack.io", "eu-api.contentstack.com") * @return Builder instance */ - public Builder setOAuthWithPKCE(String appId, String clientId, String redirectUri, String host) { + public Builder setOAuth(String appId, String clientId, String clientSecret, String redirectUri, String host) { OAuthConfig.OAuthConfigBuilder builder = OAuthConfig.builder() .appId(appId) .clientId(clientId) .redirectUri(redirectUri) .host(host); + // Only set clientSecret if provided (otherwise PKCE flow will be used) + if (clientSecret != null && !clientSecret.trim().isEmpty()) { + builder.clientSecret(clientSecret); + } + // Add token callback if set if (this.tokenCallback != null) { builder.tokenCallback(this.tokenCallback); diff --git a/src/main/java/com/contentstack/cms/core/Util.java b/src/main/java/com/contentstack/cms/core/Util.java index 11f3693..a07b302 100644 --- a/src/main/java/com/contentstack/cms/core/Util.java +++ b/src/main/java/com/contentstack/cms/core/Util.java @@ -61,7 +61,7 @@ public class Util { public static final String OAUTH_NO_TOKENS = "No OAuth tokens available. Please authenticate first."; public static final String OAUTH_NO_REFRESH_TOKEN = "No refresh token available"; public static final String OAUTH_EMPTY_CODE = "Authorization code cannot be null or empty"; - public static final String OAUTH_CONFIG_MISSING = "OAuth is not configured. Use Builder.setOAuth() or Builder.setOAuthWithPKCE()"; + public static final String OAUTH_CONFIG_MISSING = "OAuth is not configured. Use Builder.setOAuth() with or without clientSecret for PKCE flow"; public static final String OAUTH_REFRESH_FAILED = "Failed to refresh access token"; public static final String OAUTH_REVOKE_FAILED = "Failed to revoke authorization"; public static final String OAUTH_STATUS_FAILED = "Failed to get authorization status"; diff --git a/src/test/java/com/contentstack/cms/oauth/OAuthTest.java b/src/test/java/com/contentstack/cms/oauth/OAuthTest.java index 245d6db..660c917 100644 --- a/src/test/java/com/contentstack/cms/oauth/OAuthTest.java +++ b/src/test/java/com/contentstack/cms/oauth/OAuthTest.java @@ -80,7 +80,7 @@ public void setup() { // Create Contentstack clients pkceClient = new Contentstack.Builder() - .setOAuthWithPKCE(TEST_APP_ID, TEST_CLIENT_ID, TEST_REDIRECT_URI) + .setOAuth(TEST_APP_ID, TEST_CLIENT_ID, null, TEST_REDIRECT_URI) .build(); clientSecretClient = new Contentstack.Builder() @@ -113,7 +113,7 @@ public void testInvalidConfigurations() { // Test invalid app ID try { new Contentstack.Builder() - .setOAuthWithPKCE("", TEST_CLIENT_ID, TEST_REDIRECT_URI) + .setOAuth("", TEST_CLIENT_ID, null, TEST_REDIRECT_URI) .build(); fail("Should throw exception for empty app ID"); } catch (IllegalArgumentException e) { @@ -291,7 +291,7 @@ public void testHostStorage() { // Test host storage via PKCE builder client = new Contentstack.Builder() - .setOAuthWithPKCE(TEST_APP_ID, TEST_CLIENT_ID, TEST_REDIRECT_URI, testHost) + .setOAuth(TEST_APP_ID, TEST_CLIENT_ID, null, TEST_REDIRECT_URI, testHost) .build(); authUrl = client.getOAuthAuthorizationUrl(); From 3d91e99553846b89bec71b3768bf959d07850de4 Mon Sep 17 00:00:00 2001 From: reeshika-h Date: Mon, 15 Sep 2025 08:51:25 +0530 Subject: [PATCH 2/2] refactor: Update OAuth configuration methods to enhance clarity and support for optional client secret - Simplified setOAuth methods by removing the clientSecret parameter from the primary overloads. - Added a new setOAuth method to accommodate an optional client secret while maintaining PKCE flow. - Updated documentation to reflect changes in method signatures and clarify the use of client secret. --- .../com/contentstack/cms/Contentstack.java | 25 +++++++++++++------ .../com/contentstack/cms/oauth/OAuthTest.java | 10 ++++---- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/contentstack/cms/Contentstack.java b/src/main/java/com/contentstack/cms/Contentstack.java index c42f357..dbf3dbc 100644 --- a/src/main/java/com/contentstack/cms/Contentstack.java +++ b/src/main/java/com/contentstack/cms/Contentstack.java @@ -753,28 +753,39 @@ public Builder setTokenCallback(TokenCallback callback) { } /** - * Configures OAuth authentication. PKCE flow is automatically used when clientSecret is null or empty. + * Configures OAuth authentication with PKCE flow (no client secret) * @param appId Application ID * @param clientId Client ID - * @param clientSecret Client secret (optional). If null or empty, PKCE flow will be used * @param redirectUri Redirect URI * @return Builder instance */ - public Builder setOAuth(String appId, String clientId, String clientSecret, String redirectUri) { + public Builder setOAuth(String appId, String clientId, String redirectUri) { // Use the builder's hostname (which defaults to Util.HOST if not set) - return setOAuth(appId, clientId, clientSecret, redirectUri, this.hostname); + return setOAuth(appId, clientId, redirectUri, this.hostname); } /** - * Configures OAuth authentication with a specific host. PKCE flow is automatically used when clientSecret is null or empty. + * Configures OAuth authentication with PKCE flow (no client secret) and specific host * @param appId Application ID * @param clientId Client ID - * @param clientSecret Client secret (optional). If null or empty, PKCE flow will be used * @param redirectUri Redirect URI * @param host API host (e.g. "api.contentstack.io", "eu-api.contentstack.com") * @return Builder instance */ - public Builder setOAuth(String appId, String clientId, String clientSecret, String redirectUri, String host) { + public Builder setOAuth(String appId, String clientId, String redirectUri, String host) { + return setOAuth(appId, clientId, redirectUri, host, null); + } + + /** + * Configures OAuth authentication with optional client secret. PKCE flow is used when clientSecret is not provided. + * @param appId Application ID + * @param clientId Client ID + * @param redirectUri Redirect URI + * @param host API host (e.g. "api.contentstack.io", "eu-api.contentstack.com") + * @param clientSecret Optional client secret. If not provided, PKCE flow will be used + * @return Builder instance + */ + public Builder setOAuth(String appId, String clientId, String redirectUri, String host, String clientSecret) { OAuthConfig.OAuthConfigBuilder builder = OAuthConfig.builder() .appId(appId) .clientId(clientId) diff --git a/src/test/java/com/contentstack/cms/oauth/OAuthTest.java b/src/test/java/com/contentstack/cms/oauth/OAuthTest.java index 660c917..3b09c50 100644 --- a/src/test/java/com/contentstack/cms/oauth/OAuthTest.java +++ b/src/test/java/com/contentstack/cms/oauth/OAuthTest.java @@ -80,11 +80,11 @@ public void setup() { // Create Contentstack clients pkceClient = new Contentstack.Builder() - .setOAuth(TEST_APP_ID, TEST_CLIENT_ID, null, TEST_REDIRECT_URI) + .setOAuth(TEST_APP_ID, TEST_CLIENT_ID, TEST_REDIRECT_URI) .build(); clientSecretClient = new Contentstack.Builder() - .setOAuth(TEST_APP_ID, TEST_CLIENT_ID, TEST_CLIENT_SECRET, TEST_REDIRECT_URI) + .setOAuth(TEST_APP_ID, TEST_CLIENT_ID, TEST_REDIRECT_URI, Util.HOST, TEST_CLIENT_SECRET) .build(); } @@ -113,7 +113,7 @@ public void testInvalidConfigurations() { // Test invalid app ID try { new Contentstack.Builder() - .setOAuth("", TEST_CLIENT_ID, null, TEST_REDIRECT_URI) + .setOAuth("", TEST_CLIENT_ID, TEST_REDIRECT_URI) .build(); fail("Should throw exception for empty app ID"); } catch (IllegalArgumentException e) { @@ -282,7 +282,7 @@ public void testHostStorage() { // Test host storage via Contentstack.Builder Contentstack client = new Contentstack.Builder() - .setOAuth(TEST_APP_ID, TEST_CLIENT_ID, TEST_CLIENT_SECRET, TEST_REDIRECT_URI, testHost) + .setOAuth(TEST_APP_ID, TEST_CLIENT_ID, TEST_REDIRECT_URI, testHost, TEST_CLIENT_SECRET) .build(); String authUrl = client.getOAuthAuthorizationUrl(); @@ -291,7 +291,7 @@ public void testHostStorage() { // Test host storage via PKCE builder client = new Contentstack.Builder() - .setOAuth(TEST_APP_ID, TEST_CLIENT_ID, null, TEST_REDIRECT_URI, testHost) + .setOAuth(TEST_APP_ID, TEST_CLIENT_ID, TEST_REDIRECT_URI, testHost) .build(); authUrl = client.getOAuthAuthorizationUrl();