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
27 changes: 27 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,30 @@
## Release (2025-XX-XX)
- `core`: [v0.2.0](core/CHANGELOG.md#v020)
- **Feature:** Support for passing custom OkHttpClient objects
- `KeyFlowAuthenticator`: Add new constructors with an `OkHttpClientParam`
- Marked constructors without `OkHttpClient` param as deprecated, use new constructors with `OkHttpClient` instead
- `KeyFlowAuthenticator` implements `okhttp3.Authenticator` interface now
- added method `KeyFlowAuthenticator.authenticate()`
- Marked `KeyFlowInterceptor` class as deprecated, use `KeyFlowAuthenticator` instead
- Marked `SetupAuth` constructors and methods `SetupAuth.init()` and `SetupAuth.getAuthHandler()` as deprecated
- all other methods of `SetupAuth` are marked as `static` now, only these will remain in the future
- `iaas`: [v0.2.0](services/iaas/CHANGELOG.md#v020)
- **Feature:** Support for passing custom OkHttpClient objects
- `ApiClient`
- Added constructors with `OkHttpClient` param (recommended for production use)
- Use new `KeyFlowAuthenticator` `okhttp3.Authenticator` implementation instead of request interceptor for authentication
- `DefaultApi`: Added constructors with `OkHttpClient` param (recommended for production use)
- `IaasApi`: Added constructors with `OkHttpClient` param (recommended for production use)
- `resourcemanager`: [v0.2.0](services/resourcemanager/CHANGELOG.md#v020)
- **Feature:** Support for passing custom OkHttpClient objects
- `ApiClient`
- Added constructors with `OkHttpClient` param (recommended for production use)
- Use new `KeyFlowAuthenticator` `okhttp3.Authenticator` implementation instead of request interceptor for authentication
- `DefaultApi`: Added constructors with `OkHttpClient` param (recommended for production use)
- `ResourceManagerApi`: Added constructors with `OkHttpClient` param (recommended for production use)
- `examples`:
- Add example how to use custom `OkHttpClient` object

## Release (2025-09-09)
- `core`: [v0.1.0](core/CHANGELOG.md#v010)
- Initial onboarding of STACKIT Java SDK core lib
Expand Down
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,12 @@ Add the dependencies to your project's build file (replace `<SDK_VERSION>` with

## Examples

Examples on services, configuration and authentication possibilities can be found in the [examples folder](https://github.com/stackitcloud/stackit-sdk-java/tree/main/examples).
Examples on services, configuration and authentication possibilities can be found in the [examples folder](/examples).

> [!WARNING]
> For production usage, especially when working with multiple STACKIT SDK modules, consider passing your own `OkHttpClient`
> object (as recommended in the [OkHttpClient lib docs](https://square.github.io/okhttp/3.x/okhttp/index.html?okhttp3/OkHttpClient.html)).
> See our [custom HTTP client example](/examples/custom-http-client/src/main/java/cloud/stackit/sdk/customhttpclient/examples/CustomHttpClientExample.java) for reference.

## Authorization

Expand Down
10 changes: 10 additions & 0 deletions core/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,12 @@
## v0.2.0
- **Feature:** Support for passing custom OkHttpClient objects
- `KeyFlowAuthenticator`: Add new constructors with an `OkHttpClientParam`
- Marked constructors without `OkHttpClient` param as deprecated, use new constructors with `OkHttpClient` instead
- `KeyFlowAuthenticator` implements `okhttp3.Authenticator` interface now
- added method `KeyFlowAuthenticator.authenticate()`
- Marked `KeyFlowInterceptor` class as deprecated, use `KeyFlowAuthenticator` instead
- Marked `SetupAuth` constructors and methods `SetupAuth.init()` and `SetupAuth.getAuthHandler()` as deprecated
- all other methods of `SetupAuth` are marked as `static` now, only these will remain in the future

## v0.1.0
- Initial onboarding of STACKIT Java SDK core lib
2 changes: 1 addition & 1 deletion core/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.1.0
0.2.0
154 changes: 106 additions & 48 deletions core/src/main/java/cloud/stackit/sdk/core/KeyFlowAuthenticator.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package cloud.stackit.sdk.core;

import cloud.stackit.sdk.core.auth.SetupAuth;
import cloud.stackit.sdk.core.config.CoreConfiguration;
import cloud.stackit.sdk.core.config.EnvironmentVariables;
import cloud.stackit.sdk.core.exception.ApiException;
Expand All @@ -23,16 +24,18 @@
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import okhttp3.*;
import org.jetbrains.annotations.NotNull;

/** KeyFlowAuthenticator handles the Key Flow Authentication based on the Service Account Key. */
public class KeyFlowAuthenticator {
private final String REFRESH_TOKEN = "refresh_token";
private final String ASSERTION = "assertion";
private final String DEFAULT_TOKEN_ENDPOINT = "https://service-account.api.stackit.cloud/token";
private final long DEFAULT_TOKEN_LEEWAY = 60;
private final int CONNECT_TIMEOUT = 10;
private final int WRITE_TIMEOUT = 10;
private final int READ_TIMEOUT = 10;
/* KeyFlowAuthenticator handles the Key Flow Authentication based on the Service Account Key. */
public class KeyFlowAuthenticator implements Authenticator {
private static final String REFRESH_TOKEN = "refresh_token";
private static final String ASSERTION = "assertion";
private static final String DEFAULT_TOKEN_ENDPOINT =
"https://service-account.api.stackit.cloud/token";
private static final long DEFAULT_TOKEN_LEEWAY = 60;
private static final int CONNECT_TIMEOUT = 10;
private static final int WRITE_TIMEOUT = 10;
private static final int READ_TIMEOUT = 10;

private final OkHttpClient httpClient;
private final ServiceAccountKey saKey;
Expand All @@ -41,6 +44,100 @@ public class KeyFlowAuthenticator {
private final String tokenUrl;
private long tokenLeewayInSeconds = DEFAULT_TOKEN_LEEWAY;

/**
* Creates the initial service account and refreshes expired access token.
*
* @deprecated use constructor with OkHttpClient instead to prevent resource leaks. Will be
* removed in April 2026.
* @param cfg Configuration to set a custom token endpoint and the token expiration leeway.
* @param saKey Service Account Key, which should be used for the authentication
*/
@Deprecated
public KeyFlowAuthenticator(CoreConfiguration cfg, ServiceAccountKey saKey) {
this(new OkHttpClient(), cfg, saKey, new EnvironmentVariables());
}

/**
* Creates the initial service account and refreshes expired access token.
*
* @deprecated use constructor with OkHttpClient instead to prevent resource leaks. Will be
* removed in April 2026.
* @param cfg Configuration to set a custom token endpoint and the token expiration leeway.
* @param saKey Service Account Key, which should be used for the authentication
*/
@Deprecated
public KeyFlowAuthenticator(
CoreConfiguration cfg,
ServiceAccountKey saKey,
EnvironmentVariables environmentVariables) {
this(new OkHttpClient(), cfg, saKey, environmentVariables);
}

/**
* Creates the initial service account and refreshes expired access token.
*
* @param httpClient OkHttpClient object
* @param cfg Configuration to set a custom token endpoint and the token expiration leeway.
*/
public KeyFlowAuthenticator(OkHttpClient httpClient, CoreConfiguration cfg) throws IOException {
this(httpClient, cfg, SetupAuth.setupKeyFlow(cfg), new EnvironmentVariables());
}

/**
* Creates the initial service account and refreshes expired access token.
*
* @param httpClient OkHttpClient object
* @param cfg Configuration to set a custom token endpoint and the token expiration leeway.
* @param saKey Service Account Key, which should be used for the authentication
*/
public KeyFlowAuthenticator(
OkHttpClient httpClient, CoreConfiguration cfg, ServiceAccountKey saKey) {
this(httpClient, cfg, saKey, new EnvironmentVariables());
}

protected KeyFlowAuthenticator(
OkHttpClient httpClient,
CoreConfiguration cfg,
ServiceAccountKey saKey,
EnvironmentVariables environmentVariables) {
this.saKey = saKey;
this.gson = new Gson();
this.httpClient =
httpClient
.newBuilder()
.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
.build();

if (Utils.isStringSet(cfg.getTokenCustomUrl())) {
this.tokenUrl = cfg.getTokenCustomUrl();
} else if (Utils.isStringSet(environmentVariables.getStackitTokenBaseurl())) {
this.tokenUrl = environmentVariables.getStackitTokenBaseurl();
} else {
this.tokenUrl = DEFAULT_TOKEN_ENDPOINT;
}
if (cfg.getTokenExpirationLeeway() != null && cfg.getTokenExpirationLeeway() > 0) {
this.tokenLeewayInSeconds = cfg.getTokenExpirationLeeway();
}
}

@Override
public Request authenticate(Route route, @NotNull Response response) throws IOException {
String accessToken;
try {
accessToken = getAccessToken();
} catch (ApiException | InvalidKeySpecException e) {
throw new RuntimeException(e);
}

// Return a new request with the refreshed token
return response.request()
.newBuilder()
.header("Authorization", "Bearer " + accessToken)
.build();
}

protected static class KeyFlowTokenResponse {
@SerializedName("access_token")
private String accessToken;
Expand Down Expand Up @@ -79,45 +176,6 @@ protected String getAccessToken() {
}
}

public KeyFlowAuthenticator(CoreConfiguration cfg, ServiceAccountKey saKey) {
this(cfg, saKey, null);
}

/**
* Creates the initial service account and refreshes expired access token.
*
* @param cfg Configuration to set a custom token endpoint and the token expiration leeway.
* @param saKey Service Account Key, which should be used for the authentication
*/
public KeyFlowAuthenticator(
CoreConfiguration cfg,
ServiceAccountKey saKey,
EnvironmentVariables environmentVariables) {
this.saKey = saKey;
this.gson = new Gson();
this.httpClient =
new OkHttpClient.Builder()
.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
.build();

if (environmentVariables == null) {
environmentVariables = new EnvironmentVariables();
}

if (Utils.isStringSet(cfg.getTokenCustomUrl())) {
this.tokenUrl = cfg.getTokenCustomUrl();
} else if (Utils.isStringSet(environmentVariables.getStackitTokenBaseurl())) {
this.tokenUrl = environmentVariables.getStackitTokenBaseurl();
} else {
this.tokenUrl = DEFAULT_TOKEN_ENDPOINT;
}
if (cfg.getTokenExpirationLeeway() != null && cfg.getTokenExpirationLeeway() > 0) {
this.tokenLeewayInSeconds = cfg.getTokenExpirationLeeway();
}
}

/**
* Returns access token. If the token is expired it creates a new token.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,26 @@
import okhttp3.Response;
import org.jetbrains.annotations.NotNull;

@Deprecated
/*
* @deprecated use KeyFlowAuthenticator instead. Will be removed in April 2026.
* */
public class KeyFlowInterceptor implements Interceptor {
private final KeyFlowAuthenticator authenticator;

@Deprecated
/*
* @deprecated use KeyFlowAuthenticator instead. Will be removed in April 2026.
* */
public KeyFlowInterceptor(KeyFlowAuthenticator authenticator) {
this.authenticator = authenticator;
}

@NotNull @Override
@Deprecated
/*
* @deprecated use KeyFlowAuthenticator instead. Will be removed in April 2026.
* */
public Response intercept(Chain chain) throws IOException {

Request originalRequest = chain.request();
Expand Down
Loading