-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(java): Introduce OAuth2Client (#11)
- Loading branch information
1 parent
7200f38
commit 5deabb8
Showing
24 changed files
with
795 additions
and
169 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
/* | ||
* OpenFGA | ||
* A high performance and flexible authorization/permission engine built for developers and inspired by Google Zanzibar. | ||
* | ||
* The version of the OpenAPI document: 0.1 | ||
* Contact: [email protected] | ||
* | ||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). | ||
* https://openapi-generator.tech | ||
* Do not edit the class manually. | ||
*/ | ||
|
||
package dev.openfga.sdk.api.auth; | ||
|
||
import static dev.openfga.sdk.util.StringUtil.isNullOrWhitespace; | ||
|
||
import java.time.Instant; | ||
import java.util.Random; | ||
|
||
class AccessToken { | ||
private static final int TOKEN_EXPIRY_BUFFER_THRESHOLD_IN_SEC = 300; | ||
private static final int TOKEN_EXPIRY_JITTER_IN_SEC = | ||
300; // We add some jitter so that token refreshes are less likely to collide | ||
|
||
private final Random random = new Random(); | ||
private Instant expiresAt; | ||
|
||
private String token; | ||
|
||
public boolean isValid() { | ||
return !isNullOrWhitespace(token) | ||
&& (expiresAt == null | ||
|| expiresAt.isBefore(Instant.now() | ||
.plusSeconds(TOKEN_EXPIRY_BUFFER_THRESHOLD_IN_SEC) | ||
.plusSeconds(random.nextLong() % TOKEN_EXPIRY_JITTER_IN_SEC))); | ||
} | ||
|
||
public String getToken() { | ||
return token; | ||
} | ||
|
||
public void setExpiresAt(Instant expiresAt) { | ||
this.expiresAt = expiresAt; | ||
} | ||
|
||
public void setToken(String token) { | ||
this.token = token; | ||
} | ||
} |
85 changes: 85 additions & 0 deletions
85
src/main/java/dev/openfga/sdk/api/auth/CredentialsFlowRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
/* | ||
* OpenFGA | ||
* A high performance and flexible authorization/permission engine built for developers and inspired by Google Zanzibar. | ||
* | ||
* The version of the OpenAPI document: 0.1 | ||
* Contact: [email protected] | ||
* | ||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). | ||
* https://openapi-generator.tech | ||
* Do not edit the class manually. | ||
*/ | ||
|
||
package dev.openfga.sdk.api.auth; | ||
|
||
import com.fasterxml.jackson.annotation.JsonCreator; | ||
import com.fasterxml.jackson.annotation.JsonProperty; | ||
import com.fasterxml.jackson.annotation.JsonPropertyOrder; | ||
|
||
/** | ||
* A credentials flow request. It contains a Client ID and Secret that can be exchanged for an access token. | ||
* <p> | ||
* {@see "https://auth0.com/docs/get-started/authentication-and-authorization-flow/client-credentials-flow"} | ||
*/ | ||
@JsonPropertyOrder({ | ||
CredentialsFlowRequest.JSON_PROPERTY_CLIENT_ID, | ||
CredentialsFlowRequest.JSON_PROPERTY_CLIENT_SECRET, | ||
CredentialsFlowRequest.JSON_PROPERTY_AUDIENCE, | ||
CredentialsFlowRequest.JSON_PROPERTY_GRANT_TYPE | ||
}) | ||
class CredentialsFlowRequest { | ||
public static final String JSON_PROPERTY_CLIENT_ID = "client_id"; | ||
private String clientId; | ||
|
||
public static final String JSON_PROPERTY_CLIENT_SECRET = "client_secret"; | ||
private String clientSecret; | ||
|
||
public static final String JSON_PROPERTY_AUDIENCE = "audience"; | ||
private String audience; | ||
|
||
public static final String JSON_PROPERTY_GRANT_TYPE = "grant_type"; | ||
private String grantType; | ||
|
||
@JsonCreator | ||
public CredentialsFlowRequest() {} | ||
|
||
@JsonProperty(JSON_PROPERTY_CLIENT_ID) | ||
public String getClientId() { | ||
return clientId; | ||
} | ||
|
||
@JsonProperty(JSON_PROPERTY_CLIENT_ID) | ||
public void setClientId(String clientId) { | ||
this.clientId = clientId; | ||
} | ||
|
||
@JsonProperty(JSON_PROPERTY_CLIENT_SECRET) | ||
public String getClientSecret() { | ||
return clientSecret; | ||
} | ||
|
||
@JsonProperty(JSON_PROPERTY_CLIENT_SECRET) | ||
public void setClientSecret(String clientSecret) { | ||
this.clientSecret = clientSecret; | ||
} | ||
|
||
@JsonProperty(JSON_PROPERTY_AUDIENCE) | ||
public String getAudience() { | ||
return audience; | ||
} | ||
|
||
@JsonProperty(JSON_PROPERTY_AUDIENCE) | ||
public void setAudience(String audience) { | ||
this.audience = audience; | ||
} | ||
|
||
@JsonProperty(JSON_PROPERTY_GRANT_TYPE) | ||
public String getGrantType() { | ||
return grantType; | ||
} | ||
|
||
@JsonProperty(JSON_PROPERTY_GRANT_TYPE) | ||
public void setGrantType(String grantType) { | ||
this.grantType = grantType; | ||
} | ||
} |
88 changes: 88 additions & 0 deletions
88
src/main/java/dev/openfga/sdk/api/auth/CredentialsFlowResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
/* | ||
* OpenFGA | ||
* A high performance and flexible authorization/permission engine built for developers and inspired by Google Zanzibar. | ||
* | ||
* The version of the OpenAPI document: 0.1 | ||
* Contact: [email protected] | ||
* | ||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). | ||
* https://openapi-generator.tech | ||
* Do not edit the class manually. | ||
*/ | ||
|
||
package dev.openfga.sdk.api.auth; | ||
|
||
import com.fasterxml.jackson.annotation.JsonProperty; | ||
import com.fasterxml.jackson.annotation.JsonPropertyOrder; | ||
|
||
/** | ||
* A credentials flow response. Contains an access token that can be used to authenticate | ||
* <p> | ||
* {@see "https://auth0.com/docs/get-started/authentication-and-authorization-flow/client-credentials-flow"} | ||
*/ | ||
@JsonPropertyOrder({ | ||
CredentialsFlowResponse.JSON_PROPERTY_ACCESS_TOKEN, | ||
CredentialsFlowResponse.JSON_PROPERTY_SCOPE, | ||
CredentialsFlowResponse.JSON_PROPERTY_EXPIRES_IN, | ||
CredentialsFlowResponse.JSON_PROPERTY_TOKEN_TYPE | ||
}) | ||
class CredentialsFlowResponse { | ||
public static final String JSON_PROPERTY_ACCESS_TOKEN = "access_token"; | ||
private String accessToken; | ||
|
||
public static final String JSON_PROPERTY_SCOPE = "scope"; | ||
private String scope; | ||
|
||
public static final String JSON_PROPERTY_EXPIRES_IN = "expires_in"; | ||
private long expiresInSeconds; | ||
|
||
public static final String JSON_PROPERTY_TOKEN_TYPE = "token_type"; | ||
private String tokenType; | ||
|
||
@JsonProperty(JSON_PROPERTY_ACCESS_TOKEN) | ||
public String getAccessToken() { | ||
return accessToken; | ||
} | ||
|
||
@JsonProperty(JSON_PROPERTY_ACCESS_TOKEN) | ||
public void setAccessToken(String accessToken) { | ||
this.accessToken = accessToken; | ||
} | ||
|
||
@JsonProperty(JSON_PROPERTY_SCOPE) | ||
public String getScope() { | ||
return scope; | ||
} | ||
|
||
@JsonProperty(JSON_PROPERTY_SCOPE) | ||
public void setScope(String scope) { | ||
this.scope = scope; | ||
} | ||
|
||
/** | ||
* The expiration time, in seconds. | ||
* <p> | ||
* By the convention of RFC 6749 section 5.1, an expires_in value from a response will be understood | ||
* as a value in seconds. {@see https://datatracker.ietf.org/doc/html/rfc6749#autoid-55} | ||
* @return The expiration time, from now, in seconds | ||
*/ | ||
@JsonProperty(JSON_PROPERTY_EXPIRES_IN) | ||
public long getExpiresInSeconds() { | ||
return expiresInSeconds; | ||
} | ||
|
||
@JsonProperty(JSON_PROPERTY_EXPIRES_IN) | ||
public void setExpiresInSeconds(long expiresInSeconds) { | ||
this.expiresInSeconds = expiresInSeconds; | ||
} | ||
|
||
@JsonProperty(JSON_PROPERTY_TOKEN_TYPE) | ||
public String getTokenType() { | ||
return tokenType; | ||
} | ||
|
||
@JsonProperty(JSON_PROPERTY_TOKEN_TYPE) | ||
public void setTokenType(String tokenType) { | ||
this.tokenType = tokenType; | ||
} | ||
} |
106 changes: 106 additions & 0 deletions
106
src/main/java/dev/openfga/sdk/api/auth/OAuth2Client.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
/* | ||
* OpenFGA | ||
* A high performance and flexible authorization/permission engine built for developers and inspired by Google Zanzibar. | ||
* | ||
* The version of the OpenAPI document: 0.1 | ||
* Contact: [email protected] | ||
* | ||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). | ||
* https://openapi-generator.tech | ||
* Do not edit the class manually. | ||
*/ | ||
|
||
package dev.openfga.sdk.api.auth; | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import dev.openfga.sdk.api.client.ApiClient; | ||
import dev.openfga.sdk.api.configuration.*; | ||
import dev.openfga.sdk.errors.ApiException; | ||
import dev.openfga.sdk.errors.FgaInvalidParameterException; | ||
import java.io.IOException; | ||
import java.net.HttpURLConnection; | ||
import java.net.http.HttpClient; | ||
import java.net.http.HttpRequest; | ||
import java.net.http.HttpResponse; | ||
import java.time.Instant; | ||
import java.util.concurrent.CompletableFuture; | ||
|
||
public class OAuth2Client { | ||
private final HttpClient httpClient; | ||
private final Credentials credentials; | ||
private final ObjectMapper mapper; | ||
private final AccessToken token = new AccessToken(); | ||
private final CredentialsFlowRequest authRequest; | ||
private final String apiTokenIssuer; | ||
|
||
/** | ||
* Initializes a new instance of the {@link OAuth2Client} class | ||
* | ||
* @param configuration Configuration, including credentials, that can be used to retrieve an access tokens | ||
* @param httpClient Http client | ||
*/ | ||
public OAuth2Client(Configuration configuration, HttpClient httpClient, ObjectMapper mapper) | ||
throws FgaInvalidParameterException { | ||
this.credentials = configuration.getCredentials(); | ||
|
||
this.httpClient = httpClient; | ||
this.mapper = mapper; | ||
this.apiTokenIssuer = credentials.getClientCredentials().getApiTokenIssuer(); | ||
this.authRequest = new CredentialsFlowRequest(); | ||
this.authRequest.setClientId(credentials.getClientCredentials().getClientId()); | ||
this.authRequest.setClientSecret(credentials.getClientCredentials().getClientSecret()); | ||
this.authRequest.setAudience(credentials.getClientCredentials().getApiAudience()); | ||
this.authRequest.setGrantType("client_credentials"); | ||
} | ||
|
||
/** | ||
* Gets an access token, handling exchange when necessary. The access token is naively cached in memory until it | ||
* expires. | ||
* | ||
* @return An access token in a {@link CompletableFuture} | ||
*/ | ||
public CompletableFuture<String> getAccessToken() throws FgaInvalidParameterException, ApiException { | ||
if (!token.isValid()) { | ||
return exchangeToken().thenCompose(response -> { | ||
token.setToken(response.getAccessToken()); | ||
token.setExpiresAt(Instant.now().plusSeconds(response.getExpiresInSeconds())); | ||
return CompletableFuture.completedFuture(token.getToken()); | ||
}); | ||
} | ||
|
||
return CompletableFuture.completedFuture(token.getToken()); | ||
} | ||
|
||
/** | ||
* Exchange a client id and client secret for an access token. | ||
* @return The credentials flow response | ||
*/ | ||
private CompletableFuture<CredentialsFlowResponse> exchangeToken() | ||
throws ApiException, FgaInvalidParameterException { | ||
try { | ||
byte[] body = mapper.writeValueAsBytes(authRequest); | ||
|
||
Configuration config = new Configuration().apiUrl("https://" + apiTokenIssuer); | ||
|
||
HttpRequest request = ApiClient.requestBuilder("POST", "/oauth/token", body, config) | ||
.build(); | ||
|
||
return httpClient | ||
.sendAsync(request, HttpResponse.BodyHandlers.ofString()) | ||
.thenCompose(httpResponse -> { | ||
if (httpResponse.statusCode() != HttpURLConnection.HTTP_OK) { | ||
return CompletableFuture.failedFuture(new ApiException("exchangeToken", httpResponse)); | ||
} | ||
try { | ||
CredentialsFlowResponse response = | ||
mapper.readValue(httpResponse.body(), CredentialsFlowResponse.class); | ||
return CompletableFuture.completedFuture(response); | ||
} catch (Exception e) { | ||
return CompletableFuture.failedFuture(e); | ||
} | ||
}); | ||
} catch (IOException e) { | ||
throw new ApiException(e); | ||
} | ||
} | ||
} |
Oops, something went wrong.