diff --git a/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-client/pom.xml b/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-client/pom.xml
new file mode 100644
index 000000000..4ac78ce87
--- /dev/null
+++ b/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-client/pom.xml
@@ -0,0 +1,74 @@
+
+
+ 4.0.0
+
+ org.eclipse.digitaltwin.basyx
+ basyx.aasdiscoveryservice
+ ${revision}
+
+
+ basyx.aasdiscoveryservice-client
+ AAS Discovery Service Client
+
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.aasdiscoveryservice-core
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.aasdiscoveryservice-core
+ test
+ tests
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.client
+
+
+ org.springframework
+ spring-test
+ test
+
+
+ org.springframework.boot
+ spring-boot-test
+ test
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.aasdiscoveryservice-backend-h2
+ test
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.aasdiscoveryservice-backend
+ test
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.aasdiscoveryservice-http
+ test
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.aasdiscoveryservice-feature-authorization
+ test
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.http
+ test
+ tests
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.authorization
+ test
+ tests
+
+
+
+
diff --git a/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-client/src/main/java/org/eclipse/digitaltwin/basyx/aasdiscoveryservice/client/AuthorizedConnectedAasDiscoveryService.java b/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-client/src/main/java/org/eclipse/digitaltwin/basyx/aasdiscoveryservice/client/AuthorizedConnectedAasDiscoveryService.java
new file mode 100644
index 000000000..a78c728da
--- /dev/null
+++ b/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-client/src/main/java/org/eclipse/digitaltwin/basyx/aasdiscoveryservice/client/AuthorizedConnectedAasDiscoveryService.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (C) 2025 the Eclipse BaSyx Authors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * SPDX-License-Identifier: MIT
+ ******************************************************************************/
+
+package org.eclipse.digitaltwin.basyx.aasdiscoveryservice.client;
+
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.TokenManager;
+
+/**
+ * Provides access to an Authorized Aas Repository on a remote server
+ *
+ * @author fried
+ */
+public class AuthorizedConnectedAasDiscoveryService extends ConnectedAasDiscoveryService {
+
+ private TokenManager tokenManager;
+
+ public AuthorizedConnectedAasDiscoveryService(String baseURL, TokenManager tokenManager) {
+ super(baseURL, tokenManager);
+ this.tokenManager = tokenManager;
+ }
+
+ public TokenManager getTokenManager() {
+ return tokenManager;
+ }
+
+}
diff --git a/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-client/src/main/java/org/eclipse/digitaltwin/basyx/aasdiscoveryservice/client/ConnectedAasDiscoveryService.java b/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-client/src/main/java/org/eclipse/digitaltwin/basyx/aasdiscoveryservice/client/ConnectedAasDiscoveryService.java
new file mode 100644
index 000000000..0e61e629a
--- /dev/null
+++ b/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-client/src/main/java/org/eclipse/digitaltwin/basyx/aasdiscoveryservice/client/ConnectedAasDiscoveryService.java
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * Copyright (C) 2025 the Eclipse BaSyx Authors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * SPDX-License-Identifier: MIT
+ ******************************************************************************/
+
+package org.eclipse.digitaltwin.basyx.aasdiscoveryservice.client;
+
+import org.eclipse.digitaltwin.aas4j.v3.model.SpecificAssetId;
+import org.eclipse.digitaltwin.basyx.aasdiscoveryservice.client.internal.AssetAdministrationShellDiscoveryApi;
+import org.eclipse.digitaltwin.basyx.aasdiscoveryservice.core.AasDiscoveryService;
+import org.eclipse.digitaltwin.basyx.aasdiscoveryservice.core.model.AssetLink;
+import org.eclipse.digitaltwin.basyx.client.internal.ApiException;
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.TokenManager;
+import org.eclipse.digitaltwin.basyx.core.exceptions.*;
+import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult;
+import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo;
+import org.springframework.http.HttpStatus;
+
+import java.util.List;
+
+/**
+ * Provides access to an AAS Discovery Service on a remote server
+ *
+ * @author fried
+ */
+public class ConnectedAasDiscoveryService implements AasDiscoveryService {
+
+ private final AssetAdministrationShellDiscoveryApi discoveryApi;
+
+ public ConnectedAasDiscoveryService(String baseURL){
+ this.discoveryApi = new AssetAdministrationShellDiscoveryApi(baseURL);
+ }
+
+ public ConnectedAasDiscoveryService(String baseURL, TokenManager tokenManager){
+ this.discoveryApi = new AssetAdministrationShellDiscoveryApi(baseURL, tokenManager);
+ }
+
+ @Override
+ public CursorResult> getAllAssetAdministrationShellIdsByAssetLink(PaginationInfo pInfo, List assetIds) {
+ try{
+ return discoveryApi.getAllAssetAdministrationShellIdsByAssetLink(assetIds, pInfo.getLimit(), pInfo.getCursor());
+ } catch (ApiException e) {
+ if(e.getCode() == HttpStatus.NOT_FOUND.value()){
+ throw new AssetLinkDoesNotExistException("No matching element for given assetIds");
+ } else {
+ throw new RuntimeException("Error while getting all Asset Administration Shell IDs by Asset Link", e);
+ }
+ }
+ }
+
+ @Override
+ public List getAllAssetLinksById(String shellIdentifier) {
+ try{
+ return discoveryApi.getAllAssetLinksById(shellIdentifier);
+ } catch (ApiException e) {
+ if(e.getCode() == HttpStatus.NOT_FOUND.value()){
+ throw new AssetLinkDoesNotExistException("Element with id "+shellIdentifier+" does not exist");
+ } else {
+ throw new RuntimeException("Error while getting all Asset Links by ID", e);
+ }
+ }
+ }
+
+ @Override
+ public List createAllAssetLinksById(String shellIdentifier, List assetIds) {
+ try{
+ return discoveryApi.postAllAssetLinksById(shellIdentifier, assetIds);
+ } catch (ApiException e) {
+ if(e.getCode() == HttpStatus.CONFLICT.value()){
+ throw new CollidingAssetLinkException("Asset Links for shell "+shellIdentifier+" already exists");
+ } else {
+ throw new RuntimeException("Error while creating all Asset Links by ID", e);
+ }
+ }
+ }
+
+ @Override
+ public void deleteAllAssetLinksById(String shellIdentifier) {
+ try{
+ discoveryApi.deleteAllAssetLinksById(shellIdentifier);
+ } catch (ApiException e) {
+ if(e.getCode() == HttpStatus.NOT_FOUND.value()){
+ throw new AssetLinkDoesNotExistException("Element with id "+shellIdentifier+" does not exist");
+ } else {
+ throw new RuntimeException("Error while deleting all Asset Links by ID", e);
+ }
+ }
+ }
+}
diff --git a/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-client/src/main/java/org/eclipse/digitaltwin/basyx/aasdiscoveryservice/client/internal/AssetAdministrationShellDiscoveryApi.java b/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-client/src/main/java/org/eclipse/digitaltwin/basyx/aasdiscoveryservice/client/internal/AssetAdministrationShellDiscoveryApi.java
new file mode 100644
index 000000000..bed9b2e08
--- /dev/null
+++ b/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-client/src/main/java/org/eclipse/digitaltwin/basyx/aasdiscoveryservice/client/internal/AssetAdministrationShellDiscoveryApi.java
@@ -0,0 +1,255 @@
+/*******************************************************************************
+ * Copyright (C) 2025 the Eclipse BaSyx Authors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * SPDX-License-Identifier: MIT
+ ******************************************************************************/
+package org.eclipse.digitaltwin.basyx.aasdiscoveryservice.client.internal;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringJoiner;
+import java.util.function.Consumer;
+
+import org.eclipse.digitaltwin.aas4j.v3.dataformat.json.JsonMapperFactory;
+import org.eclipse.digitaltwin.aas4j.v3.dataformat.json.SimpleAbstractTypeResolverFactory;
+import org.eclipse.digitaltwin.aas4j.v3.model.SpecificAssetId;
+import org.eclipse.digitaltwin.basyx.aasdiscoveryservice.core.model.AssetLink;
+import org.eclipse.digitaltwin.basyx.client.internal.ApiClient;
+import org.eclipse.digitaltwin.basyx.client.internal.ApiException;
+import org.eclipse.digitaltwin.basyx.client.internal.ApiResponse;
+import org.eclipse.digitaltwin.basyx.client.internal.Pair;
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.TokenManager;
+import org.eclipse.digitaltwin.basyx.core.exceptions.AccessTokenRetrievalException;
+import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult;
+import org.eclipse.digitaltwin.basyx.http.Base64UrlEncodedIdentifier;
+import org.eclipse.digitaltwin.basyx.http.pagination.Base64UrlEncodedCursorResult;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import jakarta.annotation.Generated;
+
+@Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen")
+public class AssetAdministrationShellDiscoveryApi {
+
+ private final HttpClient httpClient;
+ private final ObjectMapper objectMapper;
+ private final String baseUri;
+ private final Consumer requestInterceptor;
+ private final Duration readTimeout;
+ private final Consumer> responseInterceptor;
+ private final Consumer> asyncResponseInterceptor;
+ private TokenManager tokenManager;
+
+ public AssetAdministrationShellDiscoveryApi() {
+ this(new ApiClient());
+ }
+
+ public AssetAdministrationShellDiscoveryApi(TokenManager tokenManager) {
+ this(new ApiClient());
+ this.tokenManager = tokenManager;
+ }
+
+ public AssetAdministrationShellDiscoveryApi(ApiClient apiClient) {
+ this.httpClient = apiClient.getHttpClient();
+ this.objectMapper = apiClient.getObjectMapper();
+ this.baseUri = apiClient.getBaseUri();
+ this.requestInterceptor = apiClient.getRequestInterceptor();
+ this.readTimeout = apiClient.getReadTimeout();
+ this.responseInterceptor = apiClient.getResponseInterceptor();
+ this.asyncResponseInterceptor = apiClient.getAsyncResponseInterceptor();
+ }
+
+ public AssetAdministrationShellDiscoveryApi(String baseUri) {
+ this(new ApiClient(HttpClient.newBuilder(), new JsonMapperFactory().create(new SimpleAbstractTypeResolverFactory().create()), baseUri));
+ }
+
+ public AssetAdministrationShellDiscoveryApi(String baseUri, TokenManager tokenManager) {
+ this(new ApiClient(HttpClient.newBuilder(), new JsonMapperFactory().create(new SimpleAbstractTypeResolverFactory().create()), baseUri));
+ this.tokenManager = tokenManager;
+ }
+
+ public CursorResult> getAllAssetAdministrationShellIdsByAssetLink(List assetIds, Integer limit, String cursor) throws ApiException {
+ ApiResponse>> response = getAllAssetAdministrationShellIdsByAssetLinkWithHttpInfo(assetIds, limit, cursor);
+ return response.getData();
+ }
+
+ public ApiResponse>> getAllAssetAdministrationShellIdsByAssetLinkWithHttpInfo(List assetIds, Integer limit, String cursor) throws ApiException {
+ HttpRequest.Builder requestBuilder = getAllShellIdsRequestBuilder(assetIds, limit, cursor);
+ try {
+ HttpResponse response = httpClient.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofInputStream());
+ if (responseInterceptor != null) responseInterceptor.accept(response);
+ if (response.statusCode() / 100 != 2) throw getApiException("getAllAssetAdministrationShellIdsByAssetLink", response);
+ return new ApiResponse<>(
+ response.statusCode(),
+ response.headers().map(),
+ objectMapper.readValue(response.body(), new TypeReference>>() {})
+ );
+ } catch (IOException | InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new ApiException(e);
+ }
+ }
+
+ private HttpRequest.Builder getAllShellIdsRequestBuilder(List assetIds, Integer limit, String cursor) throws ApiException {
+ HttpRequest.Builder builder = HttpRequest.newBuilder();
+ String path = "/lookup/shells";
+
+ List encodedAssetIds = new ArrayList<>();
+ assetIds.forEach(assetLink -> {
+ try {
+ String assetLinkJson = objectMapper.writeValueAsString(assetLink);
+ encodedAssetIds.add(new Base64UrlEncodedIdentifier(assetLinkJson).getEncodedIdentifier());
+ } catch (IOException e) {
+ throw new ApiException(e);
+ }
+ });
+
+ List queryParams = ApiClient.parameterToPairs("multi", "assetIds", encodedAssetIds);
+ queryParams.addAll(ApiClient.parameterToPairs("limit", limit));
+ queryParams.addAll(ApiClient.parameterToPairs("cursor", cursor));
+
+ StringJoiner query = new StringJoiner("&");
+ for (Pair p : queryParams) query.add(p.getName() + "=" + p.getValue());
+
+ builder.uri(URI.create(baseUri + path + (query.length() > 0 ? "?" + query : "")));
+ builder.header("Accept", "application/json");
+ addAuthorizationHeaderIfAuthIsEnabled(builder);
+ builder.method("GET", HttpRequest.BodyPublishers.noBody());
+ if (readTimeout != null) builder.timeout(readTimeout);
+ if (requestInterceptor != null) requestInterceptor.accept(builder);
+ return builder;
+ }
+
+ public List getAllAssetLinksById(String aasIdentifier) throws ApiException {
+ HttpRequest.Builder builder = HttpRequest.newBuilder();
+ builder.uri(URI.create(baseUri + "/lookup/shells/" + new Base64UrlEncodedIdentifier(aasIdentifier).getEncodedIdentifier()));
+ builder.header("Accept", "application/json");
+ addAuthorizationHeaderIfAuthIsEnabled(builder);
+ builder.method("GET", HttpRequest.BodyPublishers.noBody());
+ if (readTimeout != null) builder.timeout(readTimeout);
+ if (requestInterceptor != null) requestInterceptor.accept(builder);
+
+ try {
+ HttpResponse response = httpClient.send(builder.build(), HttpResponse.BodyHandlers.ofInputStream());
+ if (responseInterceptor != null) responseInterceptor.accept(response);
+ if (response.statusCode() / 100 != 2)
+ throw getApiException("getAllAssetLinksById", response);
+ return objectMapper.readValue(response.body(), new TypeReference>() {});
+ } catch (IOException | InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new ApiException(e);
+ }
+ }
+
+ public List postAllAssetLinksById(String aasIdentifier, List assetIds) throws ApiException {
+ HttpRequest.Builder builder = HttpRequest.newBuilder();
+ builder.uri(URI.create(baseUri + "/lookup/shells/" + new Base64UrlEncodedIdentifier(aasIdentifier).getEncodedIdentifier()));
+ builder.header("Content-Type", "application/json");
+ builder.header("Accept", "application/json");
+ addAuthorizationHeaderIfAuthIsEnabled(builder);
+
+ try {
+ byte[] body = objectMapper.writeValueAsBytes(assetIds);
+ builder.method("POST", HttpRequest.BodyPublishers.ofByteArray(body));
+ } catch (IOException e) {
+ throw new ApiException(e);
+ }
+
+ if (readTimeout != null) builder.timeout(readTimeout);
+ if (requestInterceptor != null) requestInterceptor.accept(builder);
+
+ try {
+ HttpResponse response = httpClient.send(builder.build(), HttpResponse.BodyHandlers.ofInputStream());
+ if (responseInterceptor != null) responseInterceptor.accept(response);
+ if (response.statusCode() / 100 != 2)
+ throw getApiException("postAllAssetLinksById", response);
+ return objectMapper.readValue(response.body(), new TypeReference>() {});
+ } catch (IOException | InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new ApiException(e);
+ }
+ }
+
+ public void deleteAllAssetLinksById(String aasIdentifier) throws ApiException {
+ HttpRequest.Builder builder = HttpRequest.newBuilder();
+ builder.uri(URI.create(baseUri + "/lookup/shells/" + new Base64UrlEncodedIdentifier(aasIdentifier).getEncodedIdentifier()));
+ builder.header("Accept", "application/json");
+ addAuthorizationHeaderIfAuthIsEnabled(builder);
+ builder.method("DELETE", HttpRequest.BodyPublishers.noBody());
+ if (readTimeout != null) builder.timeout(readTimeout);
+ if (requestInterceptor != null) requestInterceptor.accept(builder);
+
+ try {
+ HttpResponse response = httpClient.send(builder.build(), HttpResponse.BodyHandlers.ofInputStream());
+ if (responseInterceptor != null) responseInterceptor.accept(response);
+ if (response.statusCode() / 100 != 2)
+ throw getApiException("deleteAllAssetLinksById", response);
+ } catch (IOException | InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new ApiException(e);
+ }
+ }
+
+ public Object getDescription() throws ApiException {
+ HttpRequest.Builder builder = HttpRequest.newBuilder();
+ builder.uri(URI.create(baseUri + "/description"));
+ builder.header("Accept", "application/json");
+ addAuthorizationHeaderIfAuthIsEnabled(builder);
+ builder.method("GET", HttpRequest.BodyPublishers.noBody());
+ if (readTimeout != null) builder.timeout(readTimeout);
+ if (requestInterceptor != null) requestInterceptor.accept(builder);
+
+ try {
+ HttpResponse response = httpClient.send(builder.build(), HttpResponse.BodyHandlers.ofInputStream());
+ if (responseInterceptor != null) responseInterceptor.accept(response);
+ if (response.statusCode() / 100 != 2)
+ throw getApiException("getDescription", response);
+ return objectMapper.readValue(response.body(), Object.class);
+ } catch (IOException | InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new ApiException(e);
+ }
+ }
+
+ private ApiException getApiException(String operationId, HttpResponse response) throws IOException {
+ String body = response.body() == null ? null : new String(response.body().readAllBytes());
+ return new ApiException(response.statusCode(), operationId + " call failed with: " + response.statusCode() + " - " + (body != null ? body : "[no body]"), response.headers(), body);
+ }
+
+ private void addAuthorizationHeaderIfAuthIsEnabled(HttpRequest.Builder builder) {
+ if (tokenManager != null) {
+ try {
+ builder.header("Authorization", "Bearer " + tokenManager.getAccessToken());
+ } catch (IOException e) {
+ throw new AccessTokenRetrievalException("Unable to request access token");
+ }
+ }
+ }
+}
diff --git a/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-client/src/test/java/org/eclipse/digitaltwin/basyx/aasdiscoveryservice/client/DummyAasDiscoveryServiceComponent.java b/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-client/src/test/java/org/eclipse/digitaltwin/basyx/aasdiscoveryservice/client/DummyAasDiscoveryServiceComponent.java
new file mode 100644
index 000000000..679f38d32
--- /dev/null
+++ b/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-client/src/test/java/org/eclipse/digitaltwin/basyx/aasdiscoveryservice/client/DummyAasDiscoveryServiceComponent.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (C) 2025 the Eclipse BaSyx Authors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * SPDX-License-Identifier: MIT
+ ******************************************************************************/
+package org.eclipse.digitaltwin.basyx.aasdiscoveryservice.client;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * Spring application configured for tests.
+ *
+ * @author fried
+ *
+ */
+@SpringBootApplication(scanBasePackages = "org.eclipse.digitaltwin.basyx")
+public class DummyAasDiscoveryServiceComponent {
+
+ public static void main(String[] args) {
+ SpringApplication.run(DummyAasDiscoveryServiceComponent.class, args);
+ }
+}
diff --git a/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-client/src/test/java/org/eclipse/digitaltwin/basyx/aasdiscoveryservice/client/DummyDiscoveryServiceConfig.java b/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-client/src/test/java/org/eclipse/digitaltwin/basyx/aasdiscoveryservice/client/DummyDiscoveryServiceConfig.java
new file mode 100644
index 000000000..9d22361e2
--- /dev/null
+++ b/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-client/src/test/java/org/eclipse/digitaltwin/basyx/aasdiscoveryservice/client/DummyDiscoveryServiceConfig.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (C) 2025 the Eclipse BaSyx Authors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * SPDX-License-Identifier: MIT
+ ******************************************************************************/
+package org.eclipse.digitaltwin.basyx.aasdiscoveryservice.client;
+import org.eclipse.digitaltwin.basyx.aasdiscoveryservice.backend.h2.backend.H2CrudAasDiscoveryFactory;
+import org.eclipse.digitaltwin.basyx.aasdiscoveryservice.core.AasDiscoveryService;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Configuration for tests
+ *
+ * @author fried
+ *
+ */
+@Configuration
+public class DummyDiscoveryServiceConfig {
+
+ @Bean
+ AasDiscoveryService aasDiscoveryService(H2CrudAasDiscoveryFactory factory) {
+ return factory.create();
+ }
+
+}
diff --git a/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-client/src/test/java/org/eclipse/digitaltwin/basyx/aasdiscoveryservice/client/TestAuthorizedConnectedAasDiscovery.java b/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-client/src/test/java/org/eclipse/digitaltwin/basyx/aasdiscoveryservice/client/TestAuthorizedConnectedAasDiscovery.java
new file mode 100644
index 000000000..e46157ad7
--- /dev/null
+++ b/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-client/src/test/java/org/eclipse/digitaltwin/basyx/aasdiscoveryservice/client/TestAuthorizedConnectedAasDiscovery.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (C) 2025 the Eclipse BaSyx Authors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * SPDX-License-Identifier: MIT
+ ******************************************************************************/
+
+package org.eclipse.digitaltwin.basyx.aasdiscoveryservice.client;
+
+import org.eclipse.digitaltwin.basyx.aasdiscoveryservice.core.AasDiscoveryService;
+import org.eclipse.digitaltwin.basyx.aasdiscoveryservice.core.AasDiscoveryServiceSuite;
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.TokenManager;
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.credential.ClientCredential;
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.grant.ClientCredentialAccessTokenProvider;
+import org.junit.After;
+import org.junit.Before;
+import org.springframework.boot.SpringApplication;
+import org.springframework.context.ConfigurableApplicationContext;
+
+public class TestAuthorizedConnectedAasDiscovery extends AasDiscoveryServiceSuite {
+
+ private static ConfigurableApplicationContext appContext;
+ private static final String PROFILE = "authorization";
+
+ @Before
+ public void startAasDiscoveryService() {
+ SpringApplication application = new SpringApplication(DummyAasDiscoveryServiceComponent.class);
+ application.setAdditionalProfiles(PROFILE);
+ appContext = application.run(new String[] {});
+ }
+
+ @After
+ public void stopAasDiscoveryService() {
+ appContext.close();
+ }
+
+ @Override
+ protected AasDiscoveryService getAasDiscoveryService() {
+ return new AuthorizedConnectedAasDiscoveryService("http://localhost:7788", new TokenManager("http://localhost:9096/realms/BaSyx/protocol/openid-connect/token", new ClientCredentialAccessTokenProvider(new ClientCredential("workstation-1", "nY0mjyECF60DGzNmQUjL81XurSl8etom"))));
+ }
+
+}
+
diff --git a/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-client/src/test/java/org/eclipse/digitaltwin/basyx/aasdiscoveryservice/client/TestConnectedAasDiscovery.java b/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-client/src/test/java/org/eclipse/digitaltwin/basyx/aasdiscoveryservice/client/TestConnectedAasDiscovery.java
new file mode 100644
index 000000000..7a3e5492a
--- /dev/null
+++ b/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-client/src/test/java/org/eclipse/digitaltwin/basyx/aasdiscoveryservice/client/TestConnectedAasDiscovery.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (C) 2025 the Eclipse BaSyx Authors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * SPDX-License-Identifier: MIT
+ ******************************************************************************/
+
+package org.eclipse.digitaltwin.basyx.aasdiscoveryservice.client;
+
+import org.eclipse.digitaltwin.basyx.aasdiscoveryservice.core.AasDiscoveryService;
+import org.eclipse.digitaltwin.basyx.aasdiscoveryservice.core.AasDiscoveryServiceSuite;
+import org.junit.After;
+import org.junit.Before;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.context.ConfigurableApplicationContext;
+
+/**
+ * Tests the ConnectedAasDiscoveryService
+ *
+ * @author fried
+ */
+public class TestConnectedAasDiscovery extends AasDiscoveryServiceSuite {
+
+ private static ConfigurableApplicationContext appContext;
+
+ @Before
+ public void startAasDiscoveryService() {
+ appContext = new SpringApplicationBuilder(DummyAasDiscoveryServiceComponent.class).run(new String[] {});
+ }
+
+ @After
+ public void stopAasDiscoveryService() {
+ appContext.close();
+ }
+
+ @Override
+ protected AasDiscoveryService getAasDiscoveryService() {
+ return new ConnectedAasDiscoveryService("http://localhost:7788");
+ }
+}
diff --git a/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-client/src/test/resources/application-authorization.properties b/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-client/src/test/resources/application-authorization.properties
new file mode 100644
index 000000000..ba2278702
--- /dev/null
+++ b/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-client/src/test/resources/application-authorization.properties
@@ -0,0 +1,11 @@
+server.port=7788
+spring.application.name=AAS Discovery Service
+basyx.aasdiscoveryservice.name=aas-discovery-service
+
+basyx.backend = InMemory
+
+basyx.feature.authorization.enabled = true
+basyx.feature.authorization.type = rbac
+basyx.feature.authorization.jwtBearerTokenProvider = keycloak
+basyx.feature.authorization.rbac.file = classpath:rbac_rules.json
+spring.security.oauth2.resourceserver.jwt.issuer-uri= http://localhost:9096/realms/BaSyx
diff --git a/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-client/src/test/resources/application.properties b/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-client/src/test/resources/application.properties
new file mode 100644
index 000000000..718625f1d
--- /dev/null
+++ b/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-client/src/test/resources/application.properties
@@ -0,0 +1,5 @@
+server.port=7788
+spring.application.name=AAS Discovery Service
+basyx.aasdiscoveryservice.name=aas-discovery-service
+
+basyx.backend=InMemory
diff --git a/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-client/src/test/resources/rbac_rules.json b/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-client/src/test/resources/rbac_rules.json
new file mode 100644
index 000000000..23eb2d992
--- /dev/null
+++ b/basyx.aasdiscoveryservice/basyx.aasdiscoveryservice-client/src/test/resources/rbac_rules.json
@@ -0,0 +1,43 @@
+[
+ {
+ "role": "basyx-assetid-creator",
+ "action": "CREATE",
+ "targetInformation": {
+ "@type": "aas-discovery-service",
+ "aasIds": "*",
+ "assetIds": []
+ }
+ },
+ {
+ "role": "basyx-assetid-discoverer",
+ "action": "READ",
+ "targetInformation": {
+ "@type": "aas-discovery-service",
+ "aasIds": "*",
+ "assetIds": []
+ }
+ },
+ {
+ "role": "basyx-assetid-deleter",
+ "action": "DELETE",
+ "targetInformation": {
+ "@type": "aas-discovery-service",
+ "aasIds": "*",
+ "assetIds": []
+ }
+ },
+ {
+ "role": "basyx-aas-discoverer",
+ "action": "READ",
+ "targetInformation": {
+ "@type": "aas-discovery-service",
+ "aasIds": null,
+ "assetIds": [
+ {
+ "name": "*",
+ "value": "*"
+ }
+ ]
+ }
+ }
+]
diff --git a/basyx.aasdiscoveryservice/pom.xml b/basyx.aasdiscoveryservice/pom.xml
index 3b683c19f..92df94ddf 100644
--- a/basyx.aasdiscoveryservice/pom.xml
+++ b/basyx.aasdiscoveryservice/pom.xml
@@ -21,5 +21,6 @@
basyx.aasdiscoveryservice.component
basyx.aasdiscoveryservice-feature-authorization
basyx.aasdiscoveryservice-backend
+ basyx.aasdiscoveryservice-client
diff --git a/pom.xml b/pom.xml
index 290d0100d..07b15ef53 100644
--- a/pom.xml
+++ b/pom.xml
@@ -817,6 +817,11 @@
basyx.aasdiscoveryservice-feature-authorization
${revision}
+
+ org.eclipse.digitaltwin.basyx
+ basyx.aasdiscoveryservice-client
+ ${revision}
+
org.eclipse.digitaltwin.basyx
@@ -1292,6 +1297,12 @@
${revision}
tests
+
+ org.eclipse.digitaltwin.basyx
+ basyx.aasdiscoveryservice-core
+ ${revision}
+ tests
+
org.eclipse.digitaltwin.basyx