Skip to content

Commit a0b301a

Browse files
Copilotaaguiarz
andcommitted
Add public getAccessToken() methods to OpenFgaApi and OpenFgaClient
Co-authored-by: aaguiarz <1636576+aaguiarz@users.noreply.github.com>
1 parent 8539abf commit a0b301a

4 files changed

Lines changed: 160 additions & 0 deletions

File tree

src/main/java/dev/openfga/sdk/api/OpenFgaApi.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1248,6 +1248,29 @@ private String pathWithParams(String basePath, Object... params) {
12481248
return path.toString();
12491249
}
12501250

1251+
/**
1252+
* Get an access token asynchronously. This method is only available when using CLIENT_CREDENTIALS
1253+
* authentication. The token can be used for making API calls to other services that accept the same token.
1254+
*
1255+
* @return A CompletableFuture containing the access token
1256+
* @throws IllegalStateException when the credentials method is not CLIENT_CREDENTIALS
1257+
* @throws FgaInvalidParameterException when the configuration is invalid
1258+
* @throws ApiException when token retrieval fails
1259+
*/
1260+
public CompletableFuture<String> getAccessToken() throws IllegalStateException, FgaInvalidParameterException, ApiException {
1261+
CredentialsMethod credentialsMethod = this.configuration.getCredentials().getCredentialsMethod();
1262+
1263+
if (credentialsMethod != CredentialsMethod.CLIENT_CREDENTIALS) {
1264+
throw new IllegalStateException("getAccessToken() is only available when using CLIENT_CREDENTIALS authentication method");
1265+
}
1266+
1267+
if (oAuth2Client == null) {
1268+
throw new IllegalStateException("OAuth2Client is not initialized");
1269+
}
1270+
1271+
return oAuth2Client.getAccessToken();
1272+
}
1273+
12511274
/**
12521275
* Get an access token. Expects that configuration is valid (meaning it can
12531276
* pass {@link Configuration#assertValid()}) and expects that if the

src/main/java/dev/openfga/sdk/api/client/OpenFgaClient.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,20 @@ public void setConfiguration(ClientConfiguration configuration) throws FgaInvali
7575
this.api = new OpenFgaApi(configuration, apiClient);
7676
}
7777

78+
/**
79+
* Get an access token asynchronously. This method is only available when using CLIENT_CREDENTIALS
80+
* authentication. The token can be used for making API calls to other services that accept the same token.
81+
*
82+
* @return A CompletableFuture containing the access token
83+
* @throws IllegalStateException when the credentials method is not CLIENT_CREDENTIALS
84+
* @throws FgaInvalidParameterException when the configuration is invalid
85+
* @throws ApiException when token retrieval fails
86+
*/
87+
public CompletableFuture<String> getAccessToken() throws IllegalStateException, FgaInvalidParameterException, ApiException {
88+
configuration.assertValid();
89+
return api.getAccessToken();
90+
}
91+
7892
/* ********
7993
* Stores *
8094
**********/

src/test/java/dev/openfga/sdk/api/OpenFgaApiTest.java

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1928,4 +1928,68 @@ DEFAULT_STORE_ID, DEFAULT_AUTH_MODEL_ID, new WriteAssertionsRequest())
19281928
assertEquals(
19291929
"{\"code\":\"internal_error\",\"message\":\"Internal Server Error\"}", exception.getResponseData());
19301930
}
1931+
1932+
@Test
1933+
public void getAccessToken_withClientCredentials() throws Exception {
1934+
// Given
1935+
String accessToken = "test-access-token-12345";
1936+
String tokenResponse = String.format("{\"access_token\":\"%s\",\"token_type\":\"Bearer\",\"expires_in\":3600}", accessToken);
1937+
1938+
// Mock the token endpoint
1939+
mockHttpClient.onPost("https://auth.fga.example/oauth/token").doReturn(200, tokenResponse);
1940+
1941+
ClientCredentials clientCredentials = new ClientCredentials()
1942+
.clientId("test-client-id")
1943+
.clientSecret("test-client-secret")
1944+
.apiTokenIssuer("https://auth.fga.example")
1945+
.apiAudience("https://api.fga.example");
1946+
1947+
Configuration configuration = new Configuration()
1948+
.apiUrl("https://api.fga.example")
1949+
.credentials(new Credentials(clientCredentials));
1950+
1951+
OpenFgaApi api = new OpenFgaApi(configuration, mockApiClient);
1952+
1953+
// When
1954+
String result = api.getAccessToken().get();
1955+
1956+
// Then
1957+
assertEquals(accessToken, result);
1958+
mockHttpClient.verify().post("https://auth.fga.example/oauth/token").called();
1959+
}
1960+
1961+
@Test
1962+
public void getAccessToken_withApiToken() throws Exception {
1963+
// Given - API token configuration
1964+
ApiToken apiToken = new ApiToken("static-api-token");
1965+
Configuration configuration = new Configuration()
1966+
.apiUrl("https://api.fga.example")
1967+
.credentials(new Credentials(apiToken));
1968+
1969+
OpenFgaApi api = new OpenFgaApi(configuration, mockApiClient);
1970+
1971+
// When & Then
1972+
IllegalStateException exception = assertThrows(IllegalStateException.class, () -> {
1973+
api.getAccessToken().get();
1974+
});
1975+
1976+
assertEquals("getAccessToken() is only available when using CLIENT_CREDENTIALS authentication method", exception.getMessage());
1977+
}
1978+
1979+
@Test
1980+
public void getAccessToken_withNoCredentials() throws Exception {
1981+
// Given - No credentials configuration
1982+
Configuration configuration = new Configuration()
1983+
.apiUrl("https://api.fga.example")
1984+
.credentials(new Credentials());
1985+
1986+
OpenFgaApi api = new OpenFgaApi(configuration, mockApiClient);
1987+
1988+
// When & Then
1989+
IllegalStateException exception = assertThrows(IllegalStateException.class, () -> {
1990+
api.getAccessToken().get();
1991+
});
1992+
1993+
assertEquals("getAccessToken() is only available when using CLIENT_CREDENTIALS authentication method", exception.getMessage());
1994+
}
19311995
}

src/test/java/dev/openfga/sdk/api/client/OpenFgaClientTest.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2993,6 +2993,65 @@ public void setAuthorizationModelId() throws Exception {
29932993
+ "If this behavior ever changes, it could be a subtle breaking change.");
29942994
}
29952995

2996+
@Test
2997+
public void getAccessToken_withClientCredentials() throws Exception {
2998+
// Given
2999+
String accessToken = "client-test-token-67890";
3000+
String tokenResponse = String.format("{\"access_token\":\"%s\",\"token_type\":\"Bearer\",\"expires_in\":3600}", accessToken);
3001+
3002+
// Mock the token endpoint
3003+
mockHttpClient.onPost("https://auth.fga.example/oauth/token").doReturn(200, tokenResponse);
3004+
3005+
ClientCredentials clientCredentials = new ClientCredentials()
3006+
.clientId("test-client-id")
3007+
.clientSecret("test-client-secret")
3008+
.apiTokenIssuer("https://auth.fga.example")
3009+
.apiAudience("https://api.fga.example");
3010+
3011+
ClientConfiguration clientConfiguration = new ClientConfiguration()
3012+
.apiUrl("https://api.fga.example")
3013+
.storeId(DEFAULT_STORE_ID)
3014+
.credentials(new Credentials(clientCredentials));
3015+
3016+
var mockApiClient = mock(ApiClient.class);
3017+
when(mockApiClient.getHttpClient()).thenReturn(mockHttpClient);
3018+
when(mockApiClient.getObjectMapper()).thenReturn(new ObjectMapper());
3019+
when(mockApiClient.getHttpClientBuilder()).thenReturn(mock(HttpClient.Builder.class));
3020+
3021+
OpenFgaClient client = new OpenFgaClient(clientConfiguration, mockApiClient);
3022+
3023+
// When
3024+
String result = client.getAccessToken().get();
3025+
3026+
// Then
3027+
assertEquals(accessToken, result);
3028+
mockHttpClient.verify().post("https://auth.fga.example/oauth/token").called();
3029+
}
3030+
3031+
@Test
3032+
public void getAccessToken_withApiToken() throws Exception {
3033+
// Given - API token configuration
3034+
ApiToken apiToken = new ApiToken("static-api-token-client");
3035+
ClientConfiguration clientConfiguration = new ClientConfiguration()
3036+
.apiUrl("https://api.fga.example")
3037+
.storeId(DEFAULT_STORE_ID)
3038+
.credentials(new Credentials(apiToken));
3039+
3040+
var mockApiClient = mock(ApiClient.class);
3041+
when(mockApiClient.getHttpClient()).thenReturn(mockHttpClient);
3042+
when(mockApiClient.getObjectMapper()).thenReturn(new ObjectMapper());
3043+
when(mockApiClient.getHttpClientBuilder()).thenReturn(mock(HttpClient.Builder.class));
3044+
3045+
OpenFgaClient client = new OpenFgaClient(clientConfiguration, mockApiClient);
3046+
3047+
// When & Then - The exception is thrown directly, not wrapped in ExecutionException
3048+
IllegalStateException exception = assertThrows(IllegalStateException.class, () -> {
3049+
client.getAccessToken();
3050+
});
3051+
3052+
assertEquals("getAccessToken() is only available when using CLIENT_CREDENTIALS authentication method", exception.getMessage());
3053+
}
3054+
29963055
private Matcher<String> anyValidUUID() {
29973056
return new UUIDMatcher();
29983057
}

0 commit comments

Comments
 (0)