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
118 changes: 118 additions & 0 deletions daprdocs/content/en/java-sdk-docs/java-client/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,124 @@ try (DaprClient client = new DaprClientBuilder().build()) {

Learn more about the [Dapr Java SDK packages available to add to your Java applications](https://dapr.github.io/java-sdk/).

## Security

### App API Token Authentication

The building blocks like pubsub, input bindings, or jobs require Dapr to make incoming calls to your application, you can secure these requests using [Dapr App API Token Authentication]({{% ref app-api-token.md %}}). This ensures that only Dapr can invoke your application's endpoints.

#### Understanding the two tokens

Dapr uses two different tokens for securing communication. See [Properties]({{% ref properties.md %}}) for detailed information about both tokens:

- **`DAPR_API_TOKEN`** (Your app → Dapr sidecar): Automatically handled by the Java SDK when using `DaprClient`
- **`APP_API_TOKEN`** (Dapr → Your app): Requires server-side validation in your application

The examples below show how to implement server-side validation for `APP_API_TOKEN`.

#### Implementing server-side token validation

When using gRPC protocol, implement a server interceptor to capture the metadata.

```java
import io.grpc.Context;
import io.grpc.Contexts;
import io.grpc.Metadata;
import io.grpc.ServerCall;
import io.grpc.ServerCallHandler;
import io.grpc.ServerInterceptor;

public class SubscriberGrpcService extends AppCallbackGrpc.AppCallbackImplBase {
public static final Context.Key<Metadata> METADATA_KEY = Context.key("grpc-metadata");

// gRPC interceptor to capture metadata
public static class MetadataInterceptor implements ServerInterceptor {
@Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
ServerCall<ReqT, RespT> call,
Metadata headers,
ServerCallHandler<ReqT, RespT> next) {
Context contextWithMetadata = Context.current().withValue(METADATA_KEY, headers);
return Contexts.interceptCall(contextWithMetadata, call, headers, next);
}
}

// Your service methods go here...
}
```

Register the interceptor when building your gRPC server:

```java
Server server = ServerBuilder.forPort(port)
.intercept(new SubscriberGrpcService.MetadataInterceptor())
.addService(new SubscriberGrpcService())
.build();
server.start();
```

Then, in your service methods, extract the token from metadata:

```java
@Override
public void onTopicEvent(DaprAppCallbackProtos.TopicEventRequest request,
StreamObserver<DaprAppCallbackProtos.TopicEventResponse> responseObserver) {
try {
// Extract metadata from context
Context context = Context.current();
Metadata metadata = METADATA_KEY.get(context);

if (metadata != null) {
String apiToken = metadata.get(
Metadata.Key.of("dapr-api-token", Metadata.ASCII_STRING_MARSHALLER));

// Validate token accordingly
}

// Process the request
// ...

} catch (Throwable e) {
responseObserver.onError(e);
}
}
```

#### Using with HTTP endpoints

For HTTP-based endpoints, extract the token from the headers:

```java
@RestController
public class SubscriberController {

@PostMapping(path = "/endpoint")
public Mono<Void> handleRequest(
@RequestBody(required = false) byte[] body,
@RequestHeader Map<String, String> headers) {
return Mono.fromRunnable(() -> {
try {
// Extract the token from headers
String apiToken = headers.get("dapr-api-token");

// Validate token accordingly

// Process the request
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
}
```

#### Examples

For working examples with pubsub, bindings, and jobs:
- [PubSub with App API Token Authentication](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples/pubsub#app-api-token-authentication-optional)
- [Bindings with App API Token Authentication](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples/bindings/http#app-api-token-authentication-optional)
- [Jobs with App API Token Authentication](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples/jobs#app-api-token-authentication-optional)

## Related links
- [Java SDK examples](https://github.com/dapr/java-sdk/tree/master/examples/src/main/java/io/dapr/examples)

Expand Down
9 changes: 7 additions & 2 deletions daprdocs/content/en/java-sdk-docs/java-client/properties.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,16 @@ When these variables are set, the client will automatically use them to connect
| `DAPR_GRPC_PORT` | The gRPC port for the Dapr sidecar (legacy, `DAPR_GRPC_ENDPOINT` takes precedence) | `50001` |
| `DAPR_HTTP_PORT` | The HTTP port for the Dapr sidecar (legacy, `DAPR_HTTP_ENDPOINT` takes precedence) | `3500` |

### API Token
### API Tokens

Dapr supports two types of API tokens for securing communication:

| Environment Variable | Description | Default |
|---------------------|-------------|---------|
| `DAPR_API_TOKEN` | API token for authentication between app and Dapr sidecar. This is the same token used by the Dapr runtime for API authentication. For more details, see [Dapr API token authentication](https://docs.dapr.io/operations/security/api-token/) and [Environment variables reference](https://docs.dapr.io/reference/environment/#dapr_api_token). | `null` |
| `DAPR_API_TOKEN` | API token for authenticating requests **from your app to the Dapr sidecar**. The Java SDK automatically includes this token in requests when using `DaprClient`. | `null` |
| `APP_API_TOKEN` | API token for authenticating requests **from Dapr to your app**. When set, Dapr includes this token in the `dapr-api-token` header/metadata when calling your application (for pubsub subscribers, input bindings, or job triggers). Your application must validate this token. | `null` |

For implementation examples, see [App API Token Authentication]({{% ref java-client#app-api-token-authentication %}}). For more details, see [Dapr API token authentication](https://docs.dapr.io/operations/security/api-token/).

### gRPC Configuration

Expand Down
16 changes: 16 additions & 0 deletions examples/src/main/java/io/dapr/examples/bindings/http/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,22 @@ b95e7ad31707 confluentinc/cp-zookeeper:7.4.4 "/etc/confluent/dock…" 5 da
```
Click [here](https://github.com/wurstmeister/kafka-docker) for more information about the kafka broker server.

### App API Token Authentication (Optional)

Dapr supports API token authentication to secure communication between Dapr and your application. When using input bindings, Dapr makes incoming calls to your app, and you can validate these requests using the `APP_API_TOKEN`.

For detailed implementation with gRPC interceptors, see the [PubSub README App API Token Authentication section](../pubsub/README.md#app-api-token-authentication-optional).

For HTTP-based apps, check the `dapr-api-token` header in incoming requests. For more details, see the [Dapr App API Token Authentication documentation](https://docs.dapr.io/operations/security/app-api-token/).

**Quick setup:**

```bash
# Export tokens before running the following `dapr run` commands.
export APP_API_TOKEN="your-app-api-token"
export DAPR_API_TOKEN="your-dapr-api-token"
```

### Running the Input binding sample

The input binding sample uses the Spring Boot´s DaprApplication class for initializing the `InputBindingController`. In `InputBindingExample.java` file, you will find the `InputBindingExample` class and the `main` method. See the code snippet below:
Expand Down
16 changes: 16 additions & 0 deletions examples/src/main/java/io/dapr/examples/jobs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,22 @@ cd examples

Run `dapr init` to initialize Dapr in Self-Hosted Mode if it's not already initialized.

### App API Token Authentication (Optional)

Dapr supports API token authentication to secure communication between Dapr and your application. When using the Jobs API, Dapr makes incoming calls to your app at job trigger time, and you can validate these requests using the `APP_API_TOKEN`.

For detailed implementation with gRPC interceptors, see the [PubSub README App API Token Authentication section](../pubsub/README.md#app-api-token-authentication-optional).

For more details, see the [Dapr App API Token Authentication documentation](https://docs.dapr.io/operations/security/app-api-token/).

**Quick setup:**

```bash
# Export tokens before running the following `dapr run` commands.
export APP_API_TOKEN="your-app-api-token"
export DAPR_API_TOKEN="your-dapr-api-token"
```

### Running the example

This example uses the Java SDK Dapr client in order to **Schedule and Get** Jobs.
Expand Down
82 changes: 82 additions & 0 deletions examples/src/main/java/io/dapr/examples/pubsub/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,88 @@ cd examples

Run `dapr init` to initialize Dapr in Self-Hosted Mode if it's not already initialized.

### App API Token Authentication (Optional)

Dapr supports API token authentication to secure communication between Dapr and your application. This feature is useful for numerous APIs like pubsub, bindings, and jobs building blocks where Dapr makes incoming calls to your app.

For more details, see the [Dapr App API Token Authentication documentation](https://docs.dapr.io/operations/security/app-api-token/).

#### How it works

When `APP_API_TOKEN` is set, Dapr includes the token in the gRPC metadata header `dapr-api-token` when calling your app. Your app can validate this token to authenticate requests from Dapr.

#### Setting up tokens

Set a dapr annotation or simply export the environment variables before running your Dapr applications:

```bash
# Token for your app to authenticate requests FROM Dapr
export APP_API_TOKEN="your-app-api-token"

# Token for Dapr client to authenticate requests TO Dapr sidecar
export DAPR_API_TOKEN="your-dapr-api-token"
```

#### Using with gRPC Subscriber

The gRPC subscriber example includes a `MetadataInterceptor` (see `SubscriberGrpcService.java`) that captures the `dapr-api-token` from incoming requests:

```java
public class SubscriberGrpcService extends AppCallbackGrpc.AppCallbackImplBase {
public static final Context.Key<Metadata> METADATA_KEY = Context.key("grpc-metadata");

// gRPC interceptor to capture metadata
public static class MetadataInterceptor implements ServerInterceptor {
@Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
Context contextWithMetadata = Context.current().withValue(METADATA_KEY, headers);
return Contexts.interceptCall(contextWithMetadata, call, headers, next);
}
}
}
```

Then in your service methods, you can extract and validate the token:

```java
Context context = Context.current();
Metadata metadata = METADATA_KEY.get(context);
String apiToken = metadata.get(Metadata.Key.of("dapr-api-token", Metadata.ASCII_STRING_MARSHALLER));

// Validate token accordingly
```

#### Using with HTTP Subscriber

For HTTP-based endpoints, extract the token from the headers:

```java
@RestController
public class SubscriberController {

@PostMapping(path = "/endpoint")
public Mono<Void> handleRequest(
@RequestBody(required = false) byte[] body,
@RequestHeader Map<String, String> headers) {
return Mono.fromRunnable(() -> {
try {
// Extract the token from headers
String apiToken = headers.get("dapr-api-token");

// Validate token accordingly

// Process the request
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
}
```

Then use the standard `dapr run` commands shown in the sections below. The subscriber will validate incoming requests from Dapr using `APP_API_TOKEN`, and both applications will authenticate to Dapr using `DAPR_API_TOKEN`.

### Running the publisher

The publisher is a simple Java application with a main method that uses the Dapr gRPC Client to publish 10 messages to a specific topic.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,9 @@ public static void main(String[] args) throws Exception {
int port = Integer.parseInt(cmd.getOptionValue("port"));

//start a grpc server
Server server = ServerBuilder.forPort(port)
.addService(new SubscriberGrpcService())
Server server = ServerBuilder.forPort(port)
.intercept(new SubscriberGrpcService.MetadataInterceptor())
.addService(new SubscriberGrpcService())
.addService(new BulkSubscriberGrpcService())
.build();
server.start();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@
import com.google.protobuf.Empty;
import io.dapr.v1.AppCallbackGrpc;
import io.dapr.v1.DaprAppCallbackProtos;
import io.grpc.Context;
import io.grpc.Contexts;
import io.grpc.Metadata;
import io.grpc.ServerCall;
import io.grpc.ServerCallHandler;
import io.grpc.ServerInterceptor;
import io.grpc.stub.StreamObserver;

import java.util.ArrayList;
Expand All @@ -27,6 +33,17 @@
public class SubscriberGrpcService extends AppCallbackGrpc.AppCallbackImplBase {
private final List<DaprAppCallbackProtos.TopicSubscription> topicSubscriptionList = new ArrayList<>();

public static final Context.Key<Metadata> METADATA_KEY = Context.key("grpc-metadata");
// gRPC interceptor to capture metadata
public static class MetadataInterceptor implements ServerInterceptor {
@Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
Context contextWithMetadata = Context.current().withValue(METADATA_KEY, headers);
return Contexts.interceptCall(contextWithMetadata, call, headers, next);
}
}

@Override
public void listTopicSubscriptions(Empty request,
StreamObserver<DaprAppCallbackProtos.ListTopicSubscriptionsResponse> responseObserver) {
Expand All @@ -50,6 +67,30 @@ public void listTopicSubscriptions(Empty request,
public void onTopicEvent(DaprAppCallbackProtos.TopicEventRequest request,
StreamObserver<DaprAppCallbackProtos.TopicEventResponse> responseObserver) {
try {
try {
Context context = Context.current();
Metadata metadata = METADATA_KEY.get(context);

if (metadata != null) {
System.out.println("Metadata found in context");
String apiToken = metadata.get(Metadata.Key.of("dapr-api-token", Metadata.ASCII_STRING_MARSHALLER));
if (apiToken != null) {
System.out.println("API Token extracted: " + apiToken);
} else {
System.out.println("No 'dapr-api-token' found in metadata");
}
System.out.println("All metadata:");
for (String key : metadata.keys()) {
String value = metadata.get(Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER));
System.out.println("key: " + key + ": " + value);
}
} else {
System.out.println("No metadata found in context");
}
} catch (Exception e) {
System.out.println(" Error extracting metadata: " + e.getMessage());
}

String data = request.getData().toStringUtf8().replace("\"", "");
System.out.println("Subscriber got: " + data);
DaprAppCallbackProtos.TopicEventResponse response = DaprAppCallbackProtos.TopicEventResponse.newBuilder()
Expand Down
Loading