2121import com .google .common .collect .ImmutableSet ;
2222import com .google .gson .JsonElement ;
2323import io .cdap .plugin .http .common .BaseHttpConfig ;
24- import io .cdap .plugin .http .common .OAuth2ClientAuthentication ;
2524import io .cdap .plugin .http .common .OAuth2GrantType ;
2625import io .cdap .plugin .http .common .pagination .page .JSONUtil ;
2726import io .cdap .plugin .http .source .common .BaseHttpSourceConfig ;
3130import org .apache .http .client .utils .URIBuilder ;
3231import org .apache .http .impl .client .CloseableHttpClient ;
3332import org .apache .http .impl .client .HttpClients ;
33+ import org .apache .http .message .BasicHeader ;
3434import org .apache .http .message .BasicNameValuePair ;
3535import org .apache .http .util .EntityUtils ;
3636
4444import java .time .Duration ;
4545import java .time .Instant ;
4646import java .util .ArrayList ;
47+ import java .util .Base64 ;
4748import java .util .Date ;
4849import java .util .List ;
49- import java .util .Objects ;
5050import javax .annotation .Nullable ;
5151
5252/**
5353 * A class which contains utilities to make OAuth2 specific calls.
5454 */
5555public class OAuthUtil {
5656
57+ private static final String PARAM_GRANT_TYPE = "grant_type" ;
58+ private static final String PARAM_CLIENT_ID = "client_id" ;
59+ private static final String PARAM_CLIENT_SECRET = "client_secret" ;
60+ private static final String PARAM_REFRESH_TOKEN = "refresh_token" ;
61+ private static final String PARAM_SCOPE = "scope" ;
62+
5763 /**
5864 * Get Authorization header based on the config parameters provided
5965 *
@@ -116,8 +122,8 @@ public static AccessToken getAccessToken(CloseableHttpClient httpclient, BaseHtt
116122 * Retrieves an OAuth2 access token using the Client Credentials grant type.
117123 *
118124 * <p>This method constructs an HTTP POST request to fetch an access token from the authorization
119- * server. The client authentication method (either "BODY" or "REQUEST") determines whether client
120- * credentials are sent in the request body or as query parameters in the URL .</p>
125+ * server. The client authentication method (either "BODY" or "REQUEST" or "BASIC_AUTH_HEADER" ) determines whether
126+ * client credentials are sent in the request body or as query parameters or as basic auth header .</p>
121127 *
122128 * <p>Steps:
123129 * 1. If client authentication is set to "BODY": - Constructs a URI using the token URL. - Adds
@@ -127,7 +133,11 @@ public static AccessToken getAccessToken(CloseableHttpClient httpclient, BaseHtt
127133 * 2. If client authentication is set to "REQUEST": - Constructs a URI with client credentials as
128134 * query parameters. - Creates an HTTP POST request with the URI.
129135 * <br>
130- * 3. Calls `fetchAccessToken(httpclient,httppost)` to execute the request and retrieve the
136+ * 3. If client authentication is set to "BASIC_AUTH_HEADER": - Constructs a URI with client credentials first
137+ * concatenated and encoded to Base64 and passed a Basic Authorization Header and
138+ * grant type and scope as part of body.
139+ * <br>
140+ * 4. Calls `fetchAccessToken(httpclient,httppost)` to execute the request and retrieve the
131141 * token.
132142 *
133143 * @param httpclient The HTTP client to execute the request.
@@ -139,29 +149,50 @@ public static AccessToken getAccessTokenByClientCredentials(CloseableHttpClient
139149 BaseHttpConfig config ) throws IOException {
140150 URI uri ;
141151 HttpPost httppost ;
152+
142153 try {
143- if (Objects .equals (config .getOauth2ClientAuthentication ().getValue (),
144- OAuth2ClientAuthentication .BODY .getValue ())) {
145- uri = new URIBuilder (config .getTokenUrl ()).build ();
146- List <BasicNameValuePair > nameValuePairs = new ArrayList <>();
147- nameValuePairs .add (
148- new BasicNameValuePair ("grant_type" , OAuth2GrantType .CLIENT_CREDENTIALS .getValue ()));
149- nameValuePairs .add (new BasicNameValuePair ("client_id" , config .getClientId ()));
150- nameValuePairs .add (new BasicNameValuePair ("client_secret" , config .getClientSecret ()));
151- if (!Strings .isNullOrEmpty (config .getScopes ())) {
152- nameValuePairs .add (new BasicNameValuePair ("scope" , config .getScopes ()));
153- }
154- httppost = new HttpPost (uri );
155- httppost .setEntity (new UrlEncodedFormEntity (nameValuePairs ));
156- } else {
157- URIBuilder uriBuilder = new URIBuilder (config .getTokenUrl ()).setParameter ("client_id" ,
158- config .getClientId ()).setParameter ("client_secret" , config .getClientSecret ())
159- .setParameter ("grant_type" , OAuth2GrantType .CLIENT_CREDENTIALS .getValue ());
160- if (!Strings .isNullOrEmpty (config .getScopes ())) {
161- uriBuilder .setParameter ("scope" , config .getScopes ());
162- }
163- uri = uriBuilder .build ();
164- httppost = new HttpPost (uri );
154+ List <BasicNameValuePair > nameValuePairs = new ArrayList <>();
155+ switch (config .getOauth2ClientAuthentication ()) {
156+ case BODY :
157+ uri = new URIBuilder (config .getTokenUrl ()).build ();
158+ nameValuePairs .add (
159+ new BasicNameValuePair (PARAM_GRANT_TYPE , OAuth2GrantType .CLIENT_CREDENTIALS .getValue ()));
160+ nameValuePairs .add (new BasicNameValuePair (PARAM_CLIENT_ID , config .getClientId ()));
161+ nameValuePairs .add (new BasicNameValuePair (PARAM_CLIENT_SECRET , config .getClientSecret ()));
162+ if (!Strings .isNullOrEmpty (config .getScopes ())) {
163+ nameValuePairs .add (new BasicNameValuePair (PARAM_SCOPE , config .getScopes ()));
164+ }
165+ httppost = new HttpPost (uri );
166+ httppost .setEntity (new UrlEncodedFormEntity (nameValuePairs ));
167+ break ;
168+
169+ case REQUEST_PARAMETER :
170+ URIBuilder uriBuilder = new URIBuilder (config .getTokenUrl ()).setParameter (PARAM_CLIENT_ID ,
171+ config .getClientId ())
172+ .setParameter (PARAM_CLIENT_SECRET , config .getClientSecret ())
173+ .setParameter (PARAM_GRANT_TYPE , OAuth2GrantType .CLIENT_CREDENTIALS .getValue ());
174+ if (!Strings .isNullOrEmpty (config .getScopes ())) {
175+ uriBuilder .setParameter (PARAM_SCOPE , config .getScopes ());
176+ }
177+ uri = uriBuilder .build ();
178+ httppost = new HttpPost (uri );
179+ break ;
180+
181+ case BASIC_AUTH_HEADER :
182+ String credentials = config .getClientId () + ":" + config .getClientSecret ();
183+ String basicAuthHeader = String .format ("Basic %s" , Base64 .getEncoder ()
184+ .encodeToString (credentials .getBytes (StandardCharsets .UTF_8 )));
185+ nameValuePairs .add (new BasicNameValuePair (PARAM_SCOPE , config .getScopes ()));
186+ nameValuePairs .add (new BasicNameValuePair (PARAM_GRANT_TYPE , OAuth2GrantType .CLIENT_CREDENTIALS .getValue ()));
187+ uri = new URIBuilder (config .getTokenUrl ()).build ();
188+ httppost = new HttpPost (uri );
189+ httppost .setHeader (new BasicHeader ("Authorization" , basicAuthHeader ));
190+ httppost .setEntity (new UrlEncodedFormEntity (nameValuePairs ));
191+ break ;
192+ default :
193+ throw new IllegalArgumentException (
194+ String .format ("Unknown OAuth client authentication '%s'" ,
195+ config .getOauth2ClientAuthentication ().getValue ()));
165196 }
166197 return fetchAccessToken (httpclient , httppost );
167198 } catch (URISyntaxException e ) {
@@ -205,10 +236,10 @@ public static AccessToken getAccessTokenByRefreshToken(CloseableHttpClient httpc
205236 URI uri ;
206237 try {
207238 uri = new URIBuilder (config .getTokenUrl ())
208- .setParameter ("client_id" , config .getClientId ())
209- .setParameter ("client_secret" , config .getClientSecret ())
210- .setParameter ("refresh_token" , config .getRefreshToken ())
211- .setParameter ("grant_type" , "refresh_token" )
239+ .setParameter (PARAM_CLIENT_ID , config .getClientId ())
240+ .setParameter (PARAM_CLIENT_SECRET , config .getClientSecret ())
241+ .setParameter (PARAM_REFRESH_TOKEN , config .getRefreshToken ())
242+ .setParameter (PARAM_GRANT_TYPE , OAuth2GrantType . REFRESH_TOKEN . getValue () )
212243 .build ();
213244 HttpPost httppost = new HttpPost (uri );
214245 return fetchAccessToken (httpclient , httppost );
@@ -232,7 +263,16 @@ private static AccessToken fetchAccessToken(CloseableHttpClient httpclient, Http
232263
233264 JsonElement accessTokenElement = JSONUtil .toJsonObject (responseString ).get ("access_token" );
234265 if (accessTokenElement == null ) {
235- throw new IllegalArgumentException ("Access token not found" );
266+ String errorResponse ;
267+ if (response .getStatusLine () != null ) {
268+ errorResponse = String .format ("Response Code: '%s', Error Message:'%s'." ,
269+ response .getStatusLine ().getStatusCode (),
270+ response .getStatusLine ().getReasonPhrase ());
271+ } else {
272+ errorResponse = response .toString ();
273+ }
274+ throw new IllegalArgumentException (
275+ "Access token not found with Details: " + errorResponse );
236276 }
237277
238278 JsonElement expiresInElement = JSONUtil .toJsonObject (responseString ).get ("expires_in" );
0 commit comments