Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,22 @@ Authentication schemes defined for the API:
- **Authorization URL**:
- **Scopes**: N/A

<a id="custom-token-url"></a>
### Custom Token URL

By default, access tokens are requested from the token URL defined by the API. To use a different token endpoint, call `setTokenUrl` on the `ApiClient` before making a request:

```java
ApiClient client = new ApiClient("YOUR_CLIENT_ID", "YOUR_CLIENT_SECRET", null);
client.setTokenUrl("https://your-token-endpoint.example.com/oauth2/token");
```

Pass that client to any API class as usual. Note that:

- The value must be an absolute URL. Anything else throws an `IllegalArgumentException`.
- Any access token already acquired from the previous token URL is discarded, so the next request acquires a new one from the endpoint you set.
- The client must have been created with credentials (as above). Calling `setTokenUrl` on a client built without them throws a `RuntimeException`.


## Recommendation

Expand Down
20 changes: 20 additions & 0 deletions custom_templates/libraries/okhttp-gson/ApiClient.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,26 @@ public class ApiClient {
throw new RuntimeException("No OAuth2 authentication configured!");
}

{{#hasOAuthMethods}}
/**
* Helper method to set the token URL for the first OAuth2 authentication.
* Any access token acquired from the previous token URL is discarded, so the
* next request will acquire a new one from the given endpoint.
*
* @param tokenUrl The token URL. The value must be an absolute URL.
* @throws java.lang.IllegalArgumentException If the token URL is not an absolute URL
*/
public void setTokenUrl(String tokenUrl) {
for (Authentication auth : authentications.values()) {
if (auth instanceof RetryingOAuth) {
((RetryingOAuth) auth).setTokenUrl(tokenUrl);
return;
}
}
throw new RuntimeException("No OAuth2 authentication configured!");
}
{{/hasOAuthMethods}}

/**
* Helper method to set credentials for AWSV4 Signature
*
Expand Down
18 changes: 18 additions & 0 deletions custom_templates/libraries/okhttp-gson/README.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,24 @@ Class | Method | HTTP request | Description
{{/isOAuth}}

{{/authMethods}}
{{#hasOAuthMethods}}
<a id="custom-token-url"></a>
### Custom Token URL

By default, access tokens are requested from the token URL defined by the API. To use a different token endpoint, call `setTokenUrl` on the `ApiClient` before making a request:

```java
ApiClient client = new ApiClient("YOUR_CLIENT_ID", "YOUR_CLIENT_SECRET", null);
client.setTokenUrl("https://your-token-endpoint.example.com/oauth2/token");
```

Pass that client to any API class as usual. Note that:

- The value must be an absolute URL. Anything else throws an `IllegalArgumentException`.
- Any access token already acquired from the previous token URL is discarded, so the next request acquires a new one from the endpoint you set.
- The client must have been created with credentials (as above). Calling `setTokenUrl` on a client built without them throws a `RuntimeException`.

{{/hasOAuthMethods}}

## Recommendation

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ public class RetryingOAuth extends OAuth implements Interceptor {
private String clientId;
private String clientSecret;

private String tokenUrl;
private OAuthFlow flow;
private Map<String, String> parameters;

/**
* @param client An OkHttp client
* @param tokenRequestBuilder A token request builder
Expand Down Expand Up @@ -70,12 +74,10 @@ public class RetryingOAuth extends OAuth implements Interceptor {
this(OAuthClientRequest.tokenLocation(tokenUrl));
this.clientId = clientId;
this.clientSecret = clientSecret;
setFlow(flow);
if (parameters != null) {
for (Map.Entry<String, String> entry : parameters.entrySet()) {
tokenRequestBuilder.setParameter(entry.getKey(), entry.getValue());
}
}
this.tokenUrl = tokenUrl;
this.flow = flow;
this.parameters = parameters;
buildTokenRequest();
}

/**
Expand All @@ -99,7 +101,17 @@ public class RetryingOAuth extends OAuth implements Interceptor {
this(client, OAuthClientRequest.tokenLocation(tokenUrl));
this.clientId = clientId;
this.clientSecret = clientSecret;
setFlow(flow);
this.tokenUrl = tokenUrl;
this.flow = flow;
this.parameters = parameters;
buildTokenRequest();
}

private void buildTokenRequest() {
this.tokenRequestBuilder = OAuthClientRequest.tokenLocation(tokenUrl);
if (flow != null) {
setFlow(flow);
}
if (parameters != null) {
for (Map.Entry<String, String> entry : parameters.entrySet()) {
tokenRequestBuilder.setParameter(entry.getKey(), entry.getValue());
Expand All @@ -113,6 +125,7 @@ public class RetryingOAuth extends OAuth implements Interceptor {
* @param flow The OAuth flow.
*/
public void setFlow(OAuthFlow flow) {
this.flow = flow;
switch(flow) {
case ACCESS_CODE:
tokenRequestBuilder.setGrantType(GrantType.AUTHORIZATION_CODE);
Expand Down Expand Up @@ -221,6 +234,32 @@ public class RetryingOAuth extends OAuth implements Interceptor {
return getAccessToken() == null || !getAccessToken().equals(requestAccessToken);
}

/**
* Gets the token URL used to acquire access tokens for this OAuth2 flow.
*
* @return The token URL
*/
public String getTokenUrl() {
return tokenUrl;
}

/**
* Sets the token URL used to acquire access tokens for this OAuth2 flow.
* Any access token acquired from the previous token URL is discarded, so the
* next request will acquire a new one from the given endpoint.
*
* @param tokenUrl The token URL. The value must be an absolute URL.
* @throws java.lang.IllegalArgumentException If the token URL is not an absolute URL
*/
public synchronized void setTokenUrl(String tokenUrl) {
if (tokenUrl == null || "".equals(tokenUrl) || !URI.create(tokenUrl).isAbsolute()) {
throw new IllegalArgumentException("OAuth2 token URL must be an absolute URL");
}
this.tokenUrl = tokenUrl;
buildTokenRequest();
setAccessToken(null);
}

/**
* Gets the token request builder
*
Expand Down
18 changes: 18 additions & 0 deletions src/main/java/com/bandwidth/sdk/ApiClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,24 @@ public void setAccessToken(String accessToken) {
throw new RuntimeException("No OAuth2 authentication configured!");
}

/**
* Helper method to set the token URL for the first OAuth2 authentication.
* Any access token acquired from the previous token URL is discarded, so the
* next request will acquire a new one from the given endpoint.
*
* @param tokenUrl The token URL. The value must be an absolute URL.
* @throws java.lang.IllegalArgumentException If the token URL is not an absolute URL
*/
public void setTokenUrl(String tokenUrl) {
for (Authentication auth : authentications.values()) {
if (auth instanceof RetryingOAuth) {
((RetryingOAuth) auth).setTokenUrl(tokenUrl);
return;
}
}
throw new RuntimeException("No OAuth2 authentication configured!");
}

/**
* Helper method to set credentials for AWSV4 Signature
*
Expand Down
53 changes: 46 additions & 7 deletions src/main/java/com/bandwidth/sdk/auth/RetryingOAuth.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ public class RetryingOAuth extends OAuth implements Interceptor {
private String clientId;
private String clientSecret;

private String tokenUrl;
private OAuthFlow flow;
private Map<String, String> parameters;

/**
* @param client An OkHttp client
* @param tokenRequestBuilder A token request builder
Expand Down Expand Up @@ -80,12 +84,10 @@ public RetryingOAuth(
this(OAuthClientRequest.tokenLocation(tokenUrl));
this.clientId = clientId;
this.clientSecret = clientSecret;
setFlow(flow);
if (parameters != null) {
for (Map.Entry<String, String> entry : parameters.entrySet()) {
tokenRequestBuilder.setParameter(entry.getKey(), entry.getValue());
}
}
this.tokenUrl = tokenUrl;
this.flow = flow;
this.parameters = parameters;
buildTokenRequest();
}

/**
Expand All @@ -109,7 +111,17 @@ public RetryingOAuth(
this(client, OAuthClientRequest.tokenLocation(tokenUrl));
this.clientId = clientId;
this.clientSecret = clientSecret;
setFlow(flow);
this.tokenUrl = tokenUrl;
this.flow = flow;
this.parameters = parameters;
buildTokenRequest();
}

private void buildTokenRequest() {
this.tokenRequestBuilder = OAuthClientRequest.tokenLocation(tokenUrl);
if (flow != null) {
setFlow(flow);
}
if (parameters != null) {
for (Map.Entry<String, String> entry : parameters.entrySet()) {
tokenRequestBuilder.setParameter(entry.getKey(), entry.getValue());
Expand All @@ -123,6 +135,7 @@ public RetryingOAuth(
* @param flow The OAuth flow.
*/
public void setFlow(OAuthFlow flow) {
this.flow = flow;
switch(flow) {
case ACCESS_CODE:
tokenRequestBuilder.setGrantType(GrantType.AUTHORIZATION_CODE);
Expand Down Expand Up @@ -231,6 +244,32 @@ public synchronized boolean updateAccessToken(String requestAccessToken) throws
return getAccessToken() == null || !getAccessToken().equals(requestAccessToken);
}

/**
* Gets the token URL used to acquire access tokens for this OAuth2 flow.
*
* @return The token URL
*/
public String getTokenUrl() {
return tokenUrl;
}

/**
* Sets the token URL used to acquire access tokens for this OAuth2 flow.
* Any access token acquired from the previous token URL is discarded, so the
* next request will acquire a new one from the given endpoint.
*
* @param tokenUrl The token URL. The value must be an absolute URL.
* @throws java.lang.IllegalArgumentException If the token URL is not an absolute URL
*/
public synchronized void setTokenUrl(String tokenUrl) {
if (tokenUrl == null || "".equals(tokenUrl) || !URI.create(tokenUrl).isAbsolute()) {
throw new IllegalArgumentException("OAuth2 token URL must be an absolute URL");
}
this.tokenUrl = tokenUrl;
buildTokenRequest();
setAccessToken(null);
}

/**
* Gets the token request builder
*
Expand Down
34 changes: 34 additions & 0 deletions src/test/java/com/bandwidth/sdk/unit/api/ApiClientTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.bandwidth.sdk.unit.api;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import okhttp3.OkHttpClient;
Expand All @@ -12,6 +13,7 @@
import static com.bandwidth.sdk.utils.TestingEnvironmentVariables.*;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.Matchers.hasKey;
import static org.hamcrest.MatcherAssert.assertThat;
Expand Down Expand Up @@ -65,6 +67,38 @@ public void testBasePathConfiguration() {
assertThat(customBasePath, equalTo(client.getBasePath()));
}

@Test
public void testSetTokenUrl() {
String customTokenUrl = "https://api.example.com/oauth2/token";
ApiClient client = new ApiClient(BW_CLIENT_ID, BW_CLIENT_SECRET, null);
RetryingOAuth oauth = (RetryingOAuth) client.getAuthentication("OAuth2");

assertThat(oauth.getTokenUrl(), notNullValue());

client.setAccessToken("stale-access-token");
client.setTokenUrl(customTokenUrl);

assertThat(oauth.getTokenUrl(), equalTo(customTokenUrl));
assertThat(oauth.getAccessToken(), nullValue());
}

@Test
public void testSetTokenUrlWithInvalidUrl() {
ApiClient client = new ApiClient(BW_CLIENT_ID, BW_CLIENT_SECRET, null);

Assertions.assertThrows(IllegalArgumentException.class, () -> client.setTokenUrl("/oauth2/token"));
Assertions.assertThrows(IllegalArgumentException.class, () -> client.setTokenUrl(null));
Assertions.assertThrows(IllegalArgumentException.class, () -> client.setTokenUrl(""));
}

@Test
public void testSetTokenUrlWithoutOAuthConfigured() {
ApiClient client = new ApiClient();

Assertions.assertThrows(RuntimeException.class,
() -> client.setTokenUrl("https://api.example.com/oauth2/token"));
}

@Test
public void testJsonGetter() {
ApiClient client = new ApiClient();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public class CallsApiTest {

@BeforeAll
public static void setUp() {
oauthClient.setAccessToken("abcd1234");
api.setCustomBaseUrl("http://127.0.0.1:4010");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public class ConferencesApiTest {

@BeforeAll
public static void setUp() {
oauthClient.setAccessToken("abcd1234");
api.setCustomBaseUrl("http://127.0.0.1:4010");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public class EndpointsApiTest {

@BeforeAll
public static void setUp() {
oauthClient.setAccessToken("abcd1234");
api.setCustomBaseUrl("http://127.0.0.1:4010");
}

Expand Down
10 changes: 3 additions & 7 deletions src/test/java/com/bandwidth/sdk/unit/api/MediaApiTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@
import com.bandwidth.sdk.ApiClient;
import com.bandwidth.sdk.ApiException;
import com.bandwidth.sdk.ApiResponse;
import com.bandwidth.sdk.Configuration;
import com.bandwidth.sdk.api.MediaApi;
import com.bandwidth.sdk.auth.HttpBasicAuth;
import com.bandwidth.sdk.model.Media;

import static com.bandwidth.sdk.utils.TestingEnvironmentVariables.*;
Expand All @@ -22,17 +20,15 @@

@SuppressWarnings("null")
public class MediaApiTest {
private static ApiClient defaultClient = Configuration.getDefaultApiClient();
private static HttpBasicAuth Basic = (HttpBasicAuth) defaultClient.getAuthentication("Basic");
private static MediaApi api = new MediaApi(defaultClient);
private static ApiClient oauthClient = new ApiClient(BW_CLIENT_ID, BW_CLIENT_SECRET, null);
private static MediaApi api = new MediaApi(oauthClient);

private static File mediaData = new File("src/test/java/com/bandwidth/sdk/fixtures/java_cat.jpeg");
private static String mediaName = "java_binary_media";

@BeforeAll
public static void setUp() {
Basic.setUsername(BW_USERNAME);
Basic.setPassword(BW_PASSWORD);
oauthClient.setAccessToken("abcd1234");
api.setCustomBaseUrl("http://127.0.0.1:4010");
}

Expand Down
Loading