@@ -51,11 +51,12 @@ public OAuthHandler(OkHttpClient httpClient, OAuthConfig config) {
5151
5252 // Validate config before proceeding
5353 config .validate ();
54- this .state = generateCodeVerifier ();
5554
56- // Generate PKCE parameters if needed
57- if (config .isPkceEnabled ()) {
58- generatePkceParameters ();
55+ // Only generate PKCE codeVerifier if clientSecret is not provided
56+ if (config .getClientSecret () == null || config .getClientSecret ().trim ().isEmpty ()) {
57+ this .codeVerifier = generateCodeVerifier ();
58+ // codeChallenge will be generated during authorize()
59+ this .codeChallenge = null ;
5960 }
6061 }
6162
@@ -72,13 +73,13 @@ private Request.Builder _getHeaders() {
7273 * @return A random URL-safe string between 43-128 characters
7374 */
7475 private String generateCodeVerifier () {
75- SecureRandom secureRandom = new SecureRandom () ;
76- byte [] randomBytes = new byte [ 32 ] ;
77- secureRandom . nextBytes ( randomBytes );
78-
79- return Base64 . getUrlEncoder ()
80- . withoutPadding ()
81- . encodeToString ( randomBytes );
76+ final String charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~" ;
77+ SecureRandom random = new SecureRandom () ;
78+ StringBuilder verifier = new StringBuilder ( );
79+ for ( int i = 0 ; i < 128 ; i ++) {
80+ verifier . append ( charset . charAt ( random . nextInt ( charset . length ())));
81+ }
82+ return verifier . toString ( );
8283 }
8384
8485 /**
@@ -91,9 +92,11 @@ private String generateCodeChallenge(String verifier) {
9192 MessageDigest digest = MessageDigest .getInstance ("SHA-256" );
9293 byte [] hash = digest .digest (verifier .getBytes (StandardCharsets .UTF_8 ));
9394
94- return Base64 .getUrlEncoder ()
95- .withoutPadding ()
96- .encodeToString (hash );
95+ String base64String = Base64 .getEncoder ().encodeToString (hash );
96+ return base64String
97+ .replace ('+' , '-' )
98+ .replace ('/' , '_' )
99+ .replaceAll ("=+$" , "" );
97100 } catch (NoSuchAlgorithmException e ) {
98101 throw new RuntimeException ("SHA-256 algorithm not available" , e );
99102 }
@@ -114,32 +117,23 @@ private void generatePkceParameters() {
114117 */
115118 public String authorize () {
116119 try {
117- StringBuilder urlBuilder = new StringBuilder ();
118-
119- // Build the authorization URL with parameters in correct order
120- urlBuilder .append (config .getFormattedAuthorizationEndpoint ())
121- .append ("?app_id=" ).append (URLEncoder .encode (config .getAppId (), "UTF-8" ))
122- .append ("&response_type=" ).append (config .getResponseType ())
123- .append ("&client_id=" ).append (URLEncoder .encode (config .getClientId (), "UTF-8" ))
124- .append ("&redirect_uri=" ).append (URLEncoder .encode (config .getRedirectUri (), "UTF-8" ));
125-
126- // Add state for CSRF protection (always needed)
127- if (this .state != null ) {
128- urlBuilder .append ("&state=" ).append (URLEncoder .encode (this .state , "UTF-8" ));
129- }
130-
131- // Add PKCE parameters if enabled
132- if (config .isPkceEnabled ()) {
120+ String baseUrl = String .format ("%s/#!/apps/%s/authorize" ,
121+ config .getFormattedAuthorizationEndpoint (),
122+ config .getAppId ());
123+
124+ StringBuilder urlBuilder = new StringBuilder (baseUrl );
125+ urlBuilder .append ("?response_type=" ).append (config .getResponseType ())
126+ .append ("&client_id=" ).append (URLEncoder .encode (config .getClientId (), "UTF-8" ));
127+
128+ if (config .getClientSecret () != null && !config .getClientSecret ().trim ().isEmpty ()) {
129+ return urlBuilder .toString ();
130+ } else {
131+ // PKCE flow: add code_challenge
132+ this .codeChallenge = generateCodeChallenge (this .codeVerifier );
133133 urlBuilder .append ("&code_challenge=" ).append (URLEncoder .encode (this .codeChallenge , "UTF-8" ))
134134 .append ("&code_challenge_method=S256" );
135+ return urlBuilder .toString ();
135136 }
136-
137- // Add scope if present
138- if (config .getScope () != null && !config .getScope ().trim ().isEmpty ()) {
139- urlBuilder .append ("&scope=" ).append (URLEncoder .encode (config .getScope (), "UTF-8" ));
140- }
141-
142- return urlBuilder .toString ();
143137 } catch (IOException e ) {
144138 throw new RuntimeException ("Failed to encode URL parameters" , e );
145139 }
@@ -154,16 +148,16 @@ public CompletableFuture<OAuthTokens> exchangeCodeForToken(String code) {
154148 return CompletableFuture .supplyAsync (() -> {
155149 try {
156150 FormBody .Builder formBuilder = new FormBody .Builder ()
157- .add ("app_id" , config .getAppId ())
158151 .add ("grant_type" , "authorization_code" )
159152 .add ("code" , code )
160- .add ("client_id " , config .getClientId ())
161- .add ("redirect_uri " , config .getRedirectUri ());
153+ .add ("redirect_uri " , config .getRedirectUri ())
154+ .add ("client_id " , config .getClientId ());
162155
163- if (config .isPkceEnabled ()) {
164- formBuilder .add ("code_verifier" , this .codeVerifier );
165- } else {
156+ // Choose between client_secret and code_verifier like JS SDK
157+ if (config .getClientSecret () != null ) {
166158 formBuilder .add ("client_secret" , config .getClientSecret ());
159+ } else {
160+ formBuilder .add ("code_verifier" , this .codeVerifier );
167161 }
168162
169163 Request request = _getHeaders ()
0 commit comments