Skip to content

Commit

Permalink
feat(java-sdk): configurable token endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
le-yams committed Nov 30, 2023
1 parent 294bab9 commit 555a773
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 30 deletions.
1 change: 1 addition & 0 deletions config/clients/java/template/README_initializing.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ public class Example {
.credentials(new Credentials(
new ClientCredentials()
.apiTokenIssuer(System.getenv("{{appUpperCaseName}}_API_TOKEN_ISSUER"))
.tokenEndpoint(System.getenv("{{appUpperCaseName}}_API_TOKEN_ENDPOINT")) // Optional, default is https://<apiTokenIssuer>/oauth/token
.apiAudience(System.getenv("{{appUpperCaseName}}_API_AUDIENCE"))
.clientId(System.getenv("{{appUpperCaseName}}_CLIENT_ID"))
.clientSecret(System.getenv("{{appUpperCaseName}}_CLIENT_SECRET"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public class ClientCredentials {
private String clientSecret;
private String apiTokenIssuer;
private String apiAudience;
private String tokenEndpoint;
public ClientCredentials() { }

Expand Down Expand Up @@ -66,4 +67,13 @@ public class ClientCredentials {
public String getApiAudience() {
return this.apiAudience;
}

public ClientCredentials tokenEndpoint(String tokenEndpoint) {
this.tokenEndpoint = tokenEndpoint;
return this;
}

public String getTokenEndpoint() {
return this.tokenEndpoint;
}
}
22 changes: 13 additions & 9 deletions config/clients/java/template/creds-OAuth2Client.java.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,29 @@ import {{configPackage}}.*;
import {{errorsPackage}}.ApiException;
import {{errorsPackage}}.FgaInvalidParameterException;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.http.HttpClient;
import java.net.URI;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Instant;
import java.util.concurrent.CompletableFuture;
import static dev.openfga.sdk.util.StringUtil.isNullOrWhitespace;

public class OAuth2Client {
private final ApiClient apiClient;
private final Credentials credentials;
private final AccessToken token = new AccessToken();
private final CredentialsFlowRequest authRequest;
private final String apiTokenIssuer;
private final String tokenEndpoint;
/**
* Initializes a new instance of the {@link OAuth2Client} class
*
* @param configuration Configuration, including credentials, that can be used to retrieve an access tokens
*/
public OAuth2Client(Configuration configuration, ApiClient apiClient) throws FgaInvalidParameterException {
this.credentials = configuration.getCredentials();
public OAuth2Client(Configuration configuration, ApiClient apiClient) {
Credentials credentials = configuration.getCredentials();
this.apiClient = apiClient;
this.apiTokenIssuer = credentials.getClientCredentials().getApiTokenIssuer();
this.tokenEndpoint = credentials.getClientCredentials().getTokenEndpoint();
this.authRequest = new CredentialsFlowRequest();
this.authRequest.setClientId(credentials.getClientCredentials().getClientId());
this.authRequest.setClientSecret(credentials.getClientCredentials().getClientSecret());
Expand Down Expand Up @@ -67,8 +66,13 @@ public class OAuth2Client {
Configuration config = new Configuration().apiUrl("https://" + apiTokenIssuer);
HttpRequest request = ApiClient.requestBuilder("POST", "/oauth/token", body, config)
.build();
HttpRequest.Builder requestBuilder = ApiClient.requestBuilder("POST", "/oauth/token", body, config);
if (!isNullOrWhitespace(tokenEndpoint)) {
requestBuilder.uri(URI.create(tokenEndpoint));
}

HttpRequest request = requestBuilder.build();

return new HttpRequestAttempt<>(request, "exchangeToken", CredentialsFlowResponse.class, apiClient, config)
.attemptHttpRequest()
Expand Down
72 changes: 51 additions & 21 deletions config/clients/java/template/creds-OAuth2ClientTest.java.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import com.pgssoft.httpclient.HttpClientMock;
import {{clientPackage}}.ApiClient;
import {{configPackage}}.*;
import {{errorsPackage}}.FgaInvalidParameterException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class OAuth2ClientTest {
Expand All @@ -19,47 +18,78 @@ class OAuth2ClientTest {
private static final String AUDIENCE = "audience";
private static final String GRANT_TYPE = "client_credentials";
private static final String API_TOKEN_ISSUER = "test.fga.dev";
private static final String POST_URL = "https://" + API_TOKEN_ISSUER + "/oauth/token";
private static final String DEFAULT_TOKEN_URL = "https://" + API_TOKEN_ISSUER + "/oauth/token";
private static final String CONFIGURED_TOKEN_URL = "https://" + API_TOKEN_ISSUER + "/another/token/endpoint";
private static final String ACCESS_TOKEN = "0123456789";
private final ObjectMapper mapper = new ObjectMapper();
private HttpClientMock mockHttpClient;
private OAuth2Client oAuth2;
@BeforeEach
public void setup() throws FgaInvalidParameterException {
mockHttpClient = new HttpClientMock();
var credentials = new Credentials(new ClientCredentials()
.clientId(CLIENT_ID)
.clientSecret(CLIENT_SECRET)
.apiAudience(AUDIENCE)
.apiTokenIssuer(API_TOKEN_ISSUER));
var configuration = new Configuration().apiUrl("").credentials(credentials);
@Test
public void exchangeTokenUsingDefaultTokenEndpoint() throws Exception {
// Given
configureDefaultClient();
String expectedPostBody = String.format(
"{\"client_id\":\"%s\",\"client_secret\":\"%s\",\"audience\":\"%s\",\"grant_type\":\"%s\"}",
CLIENT_ID, CLIENT_SECRET, AUDIENCE, GRANT_TYPE);
String responseBody = String.format("{\"access_token\":\"%s\"}", ACCESS_TOKEN);
mockHttpClient.onPost(DEFAULT_TOKEN_URL).withBody(is(expectedPostBody)).doReturn(200, responseBody);
var apiClient = mock(ApiClient.class);
when(apiClient.getHttpClient()).thenReturn(mockHttpClient);
when(apiClient.getObjectMapper()).thenReturn(mapper);
// When
String result = oAuth2.getAccessToken().get();
oAuth2 = new OAuth2Client(configuration, apiClient);
// Then
mockHttpClient.verify().post(DEFAULT_TOKEN_URL).withBody(is(expectedPostBody)).called();
assertEquals(ACCESS_TOKEN, result);
}

@Test
public void exchangeToken() throws Exception {
public void exchangeTokenWithConfiguredEndpoint() throws Exception {
// Given
configureClientWithCustomTokenEndpoint();
String expectedPostBody = String.format(
"{\"client_id\":\"%s\",\"client_secret\":\"%s\",\"audience\":\"%s\",\"grant_type\":\"%s\"}",
CLIENT_ID, CLIENT_SECRET, AUDIENCE, GRANT_TYPE);
String responseBody = String.format("{\"access_token\":\"%s\"}", ACCESS_TOKEN);
mockHttpClient.onPost(POST_URL).withBody(is(expectedPostBody)).doReturn(200, responseBody);
mockHttpClient.onPost(CONFIGURED_TOKEN_URL).withBody(is(expectedPostBody)).doReturn(200, responseBody);
// When
String result = oAuth2.getAccessToken().get();
// Then
mockHttpClient.verify().post(POST_URL).withBody(is(expectedPostBody)).called();
mockHttpClient.verify().post(DEFAULT_TOKEN_URL).notCalled();
mockHttpClient.verify().post(CONFIGURED_TOKEN_URL).withBody(is(expectedPostBody)).called();
assertEquals(ACCESS_TOKEN, result);
}

private void configureDefaultClient() throws FgaInvalidParameterException {
configureClient(new ClientCredentials()
.clientId(CLIENT_ID)
.clientSecret(CLIENT_SECRET)
.apiAudience(AUDIENCE)
.apiTokenIssuer(API_TOKEN_ISSUER));
}

private void configureClientWithCustomTokenEndpoint() throws FgaInvalidParameterException {
configureClient(new ClientCredentials()
.clientId(CLIENT_ID)
.clientSecret(CLIENT_SECRET)
.apiAudience(AUDIENCE)
.apiTokenIssuer(API_TOKEN_ISSUER)
.tokenEndpoint(CONFIGURED_TOKEN_URL));
}

private void configureClient(ClientCredentials clientCredentials) throws FgaInvalidParameterException {
mockHttpClient = new HttpClientMock();
var credentials = new Credentials(clientCredentials);
var configuration = new Configuration().apiUrl("").credentials(credentials);
var apiClient = mock(ApiClient.class);
when(apiClient.getHttpClient()).thenReturn(mockHttpClient);
when(apiClient.getObjectMapper()).thenReturn(mapper);
oAuth2 = new OAuth2Client(configuration, apiClient);
}
}

0 comments on commit 555a773

Please sign in to comment.