2626import com .nimbusds .openid .connect .sdk .op .OIDCProviderMetadata ;
2727import java .io .IOException ;
2828import java .io .InputStream ;
29+ import java .net .HttpURLConnection ;
30+ import java .net .URL ;
31+ import javax .net .ssl .HttpsURLConnection ;
2932import java .util .Date ;
3033import java .util .List ;
3134import java .util .Objects ;
@@ -42,7 +45,8 @@ public class ConfidentialClient implements OAuth2Client {
4245
4346 private static final Logger LOGGER = LoggerFactory .getLogger (ConfidentialClient .class );
4447 private final Configuration config ;
45- private final OIDCProviderMetadata providerMetadata ;
48+ private OIDCProviderMetadata providerMetadata ;
49+ private final RequestOptions requestOptions ;
4650 private TokenRequestBuilder tokenRequestBuilder ;
4751 private long jwsIssuedAt ;
4852 private long accessTokenExpireTime ;
@@ -65,6 +69,24 @@ public ConfidentialClient(final String configPath)
6569 this (new Configuration (configPath ));
6670 }
6771
72+ /**
73+ * Creates a new ConfidentialClient. When setting up the OAuth 2.0 client, this constructor reaches out to
74+ * FactSet's well-known URI to retrieve metadata about its authorization server. This information along with
75+ * information about the OAuth 2.0 client is stored and used whenever a new access token is fetched.
76+ *
77+ * @param configPath The path towards the file to pe parsed.
78+ * @param requestOptions Object that can configure options like proxy and SSL settings
79+ * @throws AuthServerMetadataContentException If Meta Issuer or Meta Token Endpoint is missing.
80+ * @throws AuthServerMetadataException If reading from URL is unsuccessful.
81+ * @throws ConfigurationException If JWK required keys are missing from the RSA or any keys with a value
82+ * that is null or an empty string.
83+ */
84+ public ConfidentialClient (final String configPath , RequestOptions requestOptions )
85+ throws AuthServerMetadataContentException , AuthServerMetadataException ,
86+ ConfigurationException {
87+ this (new Configuration (configPath ), requestOptions );
88+ }
89+
6890 /**
6991 * Creates a new ConfidentialClient. When setting up the OAuth 2.0 client, this constructor reaches out to
7092 * FactSet's well-known URI to retrieve metadata about its authorization server. This information along with
@@ -77,25 +99,28 @@ public ConfidentialClient(final String configPath)
7799 */
78100 public ConfidentialClient (final Configuration config )
79101 throws AuthServerMetadataContentException , AuthServerMetadataException {
102+ this (config , RequestOptions .builder ().build ());
103+ }
104+
105+ /**
106+ * Creates a new ConfidentialClient. When setting up the OAuth 2.0 client, this constructor reaches out to
107+ * FactSet's well-known URI to retrieve metadata about its authorization server. This information along with
108+ * information about the OAuth 2.0 client is stored and used whenever a new access token is fetched.
109+ *
110+ * @param config Configuration object.
111+ * @param requestOptions Object that can configure options like proxy and SSL settings
112+ * @throws AuthServerMetadataContentException If Meta Issuer or Meta Token Endpoint is missing.
113+ * @throws AuthServerMetadataException If reading from URL is unsuccessful.
114+ * @throws NullPointerException Unchecked exception, if config is null.
115+ */
116+ public ConfidentialClient (final Configuration config , RequestOptions requestOptions )
117+ throws AuthServerMetadataContentException , AuthServerMetadataException {
80118 Objects .requireNonNull (config , "Configuration object must not be null" );
81119 this .config = config ;
82120 LOGGER .debug ("Finished initialising configuration" );
121+ this .requestOptions = requestOptions == null ? RequestOptions .builder ().build () : requestOptions ;
83122
84- LOGGER .debug ("Attempting to get response from Well Known URI" );
85- try (InputStream stream = config .getWellKnownUrl ().openStream ()) {
86- final String providerInfo = IOUtils .readInputStreamToString (stream );
87- this .providerMetadata = OIDCProviderMetadata .parse (providerInfo );
88- } catch (final ParseException e ) {
89- throw new AuthServerMetadataContentException ("Content of WellKnownUri has errors: " +
90- config .getWellKnownUrl ().toString (), e );
91- } catch (final IOException e ) {
92- throw new AuthServerMetadataException ("Error retrieving contents from WellKnownUri: " +
93- config .getWellKnownUrl ().toString (), e );
94- }
95- LOGGER .debug ("Response received from Well Known URI" );
96-
97- this .tokenRequestBuilder =
98- new TokenRequestBuilder ().uri (this .providerMetadata .getTokenEndpointURI ());
123+ this .requestProviderMetadata ();
99124 }
100125
101126 /**
@@ -136,6 +161,25 @@ protected ConfidentialClient(final Configuration config, final TokenRequestBuild
136161 this .tokenRequestBuilder = tokReqBuilder .uri (this .providerMetadata .getTokenEndpointURI ());
137162 }
138163
164+ /**
165+ * Creates a new ConfidentialClient. When setting up the OAuth 2.0 client, this constructor reaches out to
166+ * FactSet's well-known URI to retrieve metadata about its authorization server. This information along with
167+ * information about the OAuth 2.0 client is stored and used whenever a new access token is fetched.
168+ *
169+ * @param config Configuration object.
170+ * @param tokReqBuilder The TokenRequest builder, used to build custom TokenRequest instances.
171+ * @param requestOptions Object that can configure options like proxy and SSL settings
172+ * @throws AuthServerMetadataContentException If Meta Issuer or Meta Token Endpoint is missing.
173+ * @throws AuthServerMetadataException If reading from URL is unsuccessful.
174+ * @throws NullPointerException Unchecked exception, if config is null.
175+ */
176+ protected ConfidentialClient (final Configuration config , final TokenRequestBuilder tokReqBuilder , RequestOptions requestOptions )
177+ throws AuthServerMetadataContentException ,
178+ AuthServerMetadataException {
179+ this (config , requestOptions );
180+ this .tokenRequestBuilder = tokReqBuilder .uri (this .providerMetadata .getTokenEndpointURI ());
181+ }
182+
139183 /**
140184 * Returns an access token that can be used for authentication. If the cache contains a valid access token,
141185 * it's returned. Otherwise, a new access token is retrieved from FactSet's authorization server. The access
@@ -156,6 +200,36 @@ public String getAccessToken() throws AccessTokenException, SigningJwsException
156200 return this .fetchAccessToken ();
157201 }
158202
203+ private void requestProviderMetadata () throws AuthServerMetadataContentException , AuthServerMetadataException {
204+ LOGGER .debug ("Attempting to get response from Well Known URI" );
205+ URL wellKnownURL = this .config .getWellKnownUrl ();
206+ InputStream stream ;
207+
208+ try {
209+ HttpURLConnection conn = (HttpURLConnection ) wellKnownURL .openConnection (this .requestOptions .getProxy ());
210+ if (conn instanceof HttpsURLConnection ) {
211+ HttpsURLConnection sslConn = (HttpsURLConnection ) conn ;
212+ sslConn .setHostnameVerifier (this .requestOptions .getHostnameVerifier ());
213+ sslConn .setSSLSocketFactory (this .requestOptions .getSslSocketFactory ());
214+ }
215+
216+ stream = conn .getInputStream ();
217+
218+ final String providerInfo = IOUtils .readInputStreamToString (stream );
219+ this .providerMetadata = OIDCProviderMetadata .parse (providerInfo );
220+ } catch (final ParseException e ) {
221+ throw new AuthServerMetadataContentException ("Content of WellKnownUri has errors: " +
222+ this .config .getWellKnownUrl ().toString (), e );
223+ } catch (final IOException e ) {
224+ throw new AuthServerMetadataException ("Error retrieving contents from WellKnownUri: " +
225+ this .config .getWellKnownUrl ().toString (), e );
226+ }
227+ LOGGER .debug ("Response received from Well Known URI" );
228+
229+ this .tokenRequestBuilder =
230+ new TokenRequestBuilder ().uri (this .providerMetadata .getTokenEndpointURI ());
231+ }
232+
159233 private boolean isCachedTokenValid () {
160234 if (this .accessToken == null ) {
161235 return false ;
@@ -173,6 +247,10 @@ private String fetchAccessToken() throws AccessTokenException, SigningJwsExcepti
173247 final TokenRequest tokenRequest = this .tokenRequestBuilder .signedJwt (signedJwt ).build ();
174248
175249 final HTTPRequest httpRequest = tokenRequest .toHTTPRequest ();
250+ httpRequest .setProxy (this .requestOptions .getProxy ());
251+ httpRequest .setHostnameVerifier (this .requestOptions .getHostnameVerifier ());
252+ httpRequest .setSSLSocketFactory (this .requestOptions .getSslSocketFactory ());
253+
176254 logTokenRequest (httpRequest );
177255
178256 final HTTPResponse res = httpRequest .send ();
0 commit comments