Skip to content

v6: Support OIDC authentication #438

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 31 commits into
base: v6
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
4603422
feat: implement BearerToken OIDC flow
bevzzz Aug 6, 2025
457bd7c
chore: delete old file
bevzzz Aug 6, 2025
9f456aa
feat: throw WeaviateOAuthException when authorization flow fails
bevzzz Aug 6, 2025
42637de
fix: add synchronization on ReuseTokenProvider
bevzzz Aug 6, 2025
f7ad184
feat: reuse token from NimbusTokenProvider
bevzzz Aug 6, 2025
f13083f
fix: never expire token with expiresIn=-1
bevzzz Aug 7, 2025
272aa51
refactor: create rest/grpc transport outside of if-block
bevzzz Aug 14, 2025
330a333
feat: implement ResouceOwnerPassword flow
bevzzz Aug 14, 2025
9a07f77
feat: implement ClientCredentials flow
bevzzz Aug 14, 2025
1f73f0f
chore: rename bearerToken flow to refreshToken internally
bevzzz Aug 14, 2025
78cd310
refactor: drop unused configuration
bevzzz Aug 14, 2025
01e58ea
feat: add user / default scopes
bevzzz Aug 14, 2025
6bd8056
test: finish Bearer Token test case
bevzzz Aug 14, 2025
23d149a
feat: add early expiry to tokens
bevzzz Aug 14, 2025
badbe9e
chore: fix javadoc
bevzzz Aug 14, 2025
eb5607f
chore: fix typo
bevzzz Aug 14, 2025
5fc3ce5
feat: add Authorization to WeaviateClientAsync
bevzzz Aug 14, 2025
ad6ea8b
feat: implement readiness check
bevzzz Aug 14, 2025
897225a
feat: add /meta endpoint
bevzzz Aug 14, 2025
a1615ed
feat: use non-blocking auth interceptor for async client
bevzzz Aug 17, 2025
148cb3f
feat: use non-blocking token provider in the gRPC stub
bevzzz Aug 17, 2025
b519b19
test: target latest Weavite version (1.32.3)
bevzzz Aug 17, 2025
887a64b
refactor: use shared ExecutorService in AsyncTokenProvider.Default
bevzzz Aug 17, 2025
75f8bf6
chore: add javadoc
bevzzz Aug 17, 2025
a898ff9
chore(README): add example of using shaded dependency
bevzzz Aug 18, 2025
3d0df1b
fix: refresh tokens in the background
bevzzz Aug 18, 2025
ba62298
fix: reuse old refresh_token if the new one was not returned
bevzzz Aug 18, 2025
909f1e9
chore: rename Authorization -> Authentication
bevzzz Aug 18, 2025
733bc7d
chore: add comment
bevzzz Aug 20, 2025
06661b9
feat: test that tokens are being refreshed after expiry
bevzzz Aug 21, 2025
f5ef775
refactor: remove redundant variable
bevzzz Aug 22, 2025
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
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ Official Weaviate Java Client.
To start using Weaviate Java Client add the dependency to `pom.xml`:

```xml

<dependency>
<groupId>io.weaviate</groupId>
<artifactId>client6</artifactId>
Expand All @@ -19,9 +18,17 @@ To start using Weaviate Java Client add the dependency to `pom.xml`:

### Uber JAR🫙

If you're building a uber-JAR with something like `maven-assembly-plugin`, use a shaded version with classifier `all`.
If you're building a uber-JAR with something like `maven-assembly-plugin`, use a shaded version with classifier `all`.
This ensures that all dynamically-loaded dependecies of `io.grpc` are resolved correctly.

```xml
<dependency>
<groupId>io.weaviate</groupId>
<artifactId>client6</artifactId>
<version>6.0.0-beta4</version>
<classifier>all</classifier>
</dependency>
```

### Gson and reflective access to internal JDK classes

Expand Down
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<trimStackTrace>false</trimStackTrace>
<argLine>
<!--
Gson (used for JSON serialization) utilizes reflection and needs to be able to access private fields of
Expand Down
10 changes: 0 additions & 10 deletions src/it/java/io/weaviate/containers/Container.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,6 @@ public class Container {
public static final Contextionary CONTEXTIONARY = Contextionary.createDefault();
public static final Img2VecNeural IMG2VEC_NEURAL = Img2VecNeural.createDefault();

static {
startAll();
}

/** Start all shared Testcontainers. */
// TODO: start lazily!
static void startAll() {
// WEAVIATE.start();
}

/**
* Stop all shared Testcontainers created in {@link #startAll}.
* <p>
Expand Down
120 changes: 97 additions & 23 deletions src/it/java/io/weaviate/containers/Weaviate.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,41 +6,81 @@
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;

import org.testcontainers.weaviate.WeaviateContainer;

import io.weaviate.client6.v1.api.Config;
import io.weaviate.client6.v1.api.WeaviateClient;
import io.weaviate.client6.v1.internal.ObjectBuilder;

public class Weaviate extends WeaviateContainer {
private WeaviateClient clientInstance;

public static final String VERSION = "1.29.1";
public static final String VERSION = "1.32.3";
public static final String DOCKER_IMAGE = "semitechnologies/weaviate";

private volatile SharedClient clientInstance;

public WeaviateClient getClient() {
return getClient(ObjectBuilder.identity());
}

/**
* Get a client for the current Weaviate container.
* As we aren't running tests in parallel at the moment,
* this is not made thread-safe.
* Create a new instance of WeaviateClient connected to this container if none
* exist. Get an existing client otherwise.
*
* The lifetime of this client is tied to that of its container, which means
* that you do not need to {@code close} it manually. It will only truly close
* after the parent Testcontainer is stopped.
*/
public WeaviateClient getClient() {
// FIXME: control from containers?
public WeaviateClient getClient(Function<Config.Custom, ObjectBuilder<Config>> fn) {
if (!isRunning()) {
start();
}
if (clientInstance == null) {
try {
clientInstance = WeaviateClient.local(
if (clientInstance != null) {
return clientInstance;
}

synchronized (this) {
if (clientInstance == null) {
var host = getHost();
var customFn = ObjectBuilder.partial(fn,
conn -> conn
.host(getHost())
.scheme("http")
.httpHost(host)
.grpcHost(host)
.httpPort(getMappedPort(8080))
.grpcPort(getMappedPort(50051)));
} catch (Exception e) {
throw new RuntimeException("create WeaviateClient for Weaviate container", e);
var config = customFn.apply(new Config.Custom()).build();
try {
clientInstance = new SharedClient(config, this);
} catch (Exception e) {
throw new RuntimeException("create WeaviateClient for Weaviate container", e);
}
}
}
return clientInstance;
}

/**
* Create a new instance of WeaviateClient connected to this container.
* Prefer using {@link #getClient} unless your test needs the initialization
* steps to run, e.g. OIDC authorization grant exchange.
*/
public WeaviateClient getNewClient(Function<Config.Custom, ObjectBuilder<Config>> fn) {
if (!isRunning()) {
start();
}
var host = getHost();
var customFn = ObjectBuilder.partial(fn,
conn -> conn
.scheme("http")
.httpHost(host)
.grpcHost(host)
.httpPort(getMappedPort(8080))
.grpcPort(getMappedPort(50051)));
return WeaviateClient.custom(customFn);
}

public static Weaviate createDefault() {
return new Builder().build();
}
Expand Down Expand Up @@ -90,20 +130,32 @@ public Builder withImageInference(String url, String module) {
}

public Builder enableTelemetry(boolean enable) {
telemetry = enable;
environment.put("DISABLE_TELEMETRY", Boolean.toString(!enable));
return this;
}

public Builder enableAnonymousAccess(boolean enable) {
environment.put("AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED", Boolean.toString(enable));
return this;
}

public Builder withOIDC(String clientId, String issuer, String usernameClaim, String groupsClaim) {
enableAnonymousAccess(false);
environment.put("AUTHENTICATION_OIDC_ENABLED", "true");
environment.put("AUTHENTICATION_OIDC_CLIENT_ID", clientId);
environment.put("AUTHENTICATION_OIDC_ISSUER", issuer);
environment.put("AUTHENTICATION_OIDC_USERNAME_CLAIM", usernameClaim);
environment.put("AUTHENTICATION_OIDC_GROUPS_CLAIM", groupsClaim);
return this;
}

public Weaviate build() {
var c = new Weaviate(DOCKER_IMAGE + ":" + versionTag);

if (!enableModules.isEmpty()) {
c.withEnv("ENABLE_API_BASED_MODULES", "'true'");
c.withEnv("ENABLE_API_BASED_MODULES", Boolean.TRUE.toString());
c.withEnv("ENABLE_MODULES", String.join(",", enableModules));
}
if (!telemetry) {
c.withEnv("DISABLE_TELEMETRY", "true");
}

environment.forEach((name, value) -> c.withEnv(name, value));
c.withCreateContainerCmdModifier(cmd -> cmd.withHostName("weaviate"));
Expand All @@ -125,10 +177,32 @@ public void stop() {
if (clientInstance == null) {
return;
}
try {
clientInstance.close();
} catch (IOException e) {
// TODO: log error
synchronized (this) {
try {
clientInstance.close(this);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

/** SharedClient's lifetime is tied to that of it's parent container. */
private class SharedClient extends WeaviateClient {
private final Weaviate parent;

private SharedClient(Config config, Weaviate parent) {
super(config);
this.parent = parent;
}

private void close(Weaviate caller) throws Exception {
if (caller == parent) {
super.close();
}
}

@Override
public void close() throws IOException {
}
}
}
Loading