CamundaClient: What is audience? What is the url of my authorization server?

I’m trying to construct my own CamundaClient to connect to Camunda via grpc using the snippet of code below.

Question:

  1. What is the parameter “Audience”?
  2. What is the url for the oauth server?
CredentialsProvider credentialsProvider = CredentialsProvider.newCredentialsProviderBuilder()
                .clientId("zeebe")
                .clientSecret("zeebe")
                .audience("What is audience?")
                .authorizationServerUrl("http://127.0.0.1:26500/oauth/token")
                .build();

        try (CamundaClient clt = CamundaClient.newClientBuilder()
                .grpcAddress(URI.create("http://127.0.0.1:26500"))
                .credentialsProvider(credentialsProvider)
                .build()){
}

@khew You’re trying to configure a CamundaClient (likely from the Zeebe Java client) with OAuth credentials to authenticate with Camunda.

What is the audience parameter?

In the OAuth2 context, the audience is the identifier of the resource server (in this case, Zeebe) that the client wants to access. It is used in the token request to signal which API or service the access token is intended for.

In Camunda 8 with Keycloak, the audience should match the Zeebe client audience name expected in the Keycloak configuration.

:small_blue_diamond: In most Camunda 8 setups, audience = zeebe-api

This is because:

  • The zeebe-api audience is often defined in Keycloak as a client (resource server).
  • The access token must be accepted by Zeebe.

Use this (unless you’ve customized Keycloak):

.audience("zeebe-api")

What is the correct authorizationServerUrl?

This should point to your Keycloak token endpoint, not the Zeebe gateway port (26500). You should use the token endpoint from your Keycloak realm.

For a standard local setup:

.authorizationServerUrl("http://localhost:18080/realms/camunda-platform/protocol/openid-connect/token")

Replace localhost:18080 with your actual Keycloak server host and port.

The full structure is:

http(s)://<keycloak-host>:<port>/realms/<realm-name>/protocol/openid-connect/token

If you’re using Camunda 8 Helm chart defaults:

  • Keycloak runs on port 18080
  • Realm is usually camunda-platform
  • Token URL becomes:
http://localhost:18080/realms/camunda-platform/protocol/openid-connect/token

Example

CredentialsProvider credentialsProvider = CredentialsProvider.newCredentialsProviderBuilder()
        .clientId("zeebe") // must match the Keycloak client ID
        .clientSecret("zeebe") // must match the Keycloak client secret
        .audience("zeebe-api") // matches the audience expected by Zeebe
        .authorizationServerUrl("http://localhost:18080/realms/camunda-platform/protocol/openid-connect/token")
        .build();

try (CamundaClient client = CamundaClient.newClientBuilder()
        .grpcAddress(URI.create("localhost:26500"))
        .credentialsProvider(credentialsProvider)
        .build()) {
    // Use the client here
}

Tips

  • To confirm the audience value: In Keycloak, go to Clients → zeebe → Settings → “Client ID” and look at what it exposes under aud in a JWT token.
  • Use a tool like jwt.io to inspect a token and confirm the aud claim.

Thank you so much for your reply @aravindhrs. It is very informative, however i’m still stuck.

  1. I did not find audience in my KeyCloak. The UI is a bit different, maybe because I’m using Camunda 8.8 docker deployment.
    In keycloak, under clients I see both zeebe and zeebe-api as clientId:


I cannot find audience when I opened them.

  1. Looking the clientId list above, should I change my clientId and clientSecret to values from this list? E.g. clientId = “zeebe” and clientSecret = “zecret” instead of the clientId and clientSecret we defined in Camunda Identity?

  2. I have tried different clientId, clientSecret as well as your recommended serverURL and audience. Im keep getting the following exception:

Caused by: io.grpc.StatusRuntimeException: UNAVAILABLE: io exception
Channel Pipeline: [SslHandler#0, ProtocolNegotiators$ClientTlsHandler#0, WriteBufferingAndExceptionHandler#0, DefaultChannelPipeline$TailContext#0]
	at io.grpc.Status.asRuntimeException(Status.java:532) ~[grpc-api-1.71.0.jar:1.71.0]
	at io.grpc.stub.ClientCalls$StreamObserverToCallListenerAdapter.onClose(ClientCalls.java:564) ~[grpc-stub-1.71.0.jar:1.71.0]
	at io.grpc.internal.DelayedClientCall$DelayedListener$3.run(DelayedClientCall.java:489) ~[grpc-core-1.71.0.jar:1.71.0]
	at io.grpc.internal.DelayedClientCall$DelayedListener.delayOrExecute(DelayedClientCall.java:453) ~[grpc-core-1.71.0.jar:1.71.0]
	at io.grpc.internal.DelayedClientCall$DelayedListener.onClose(DelayedClientCall.java:486) ~[grpc-core-1.71.0.jar:1.71.0]
	at io.grpc.internal.ClientCallImpl.closeObserver(ClientCallImpl.java:565) ~[grpc-core-1.71.0.jar:1.71.0]
	at io.grpc.internal.ClientCallImpl.access$100(ClientCallImpl.java:72) ~[grpc-core-1.71.0.jar:1.71.0]
	at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1StreamClosed.runInternal(ClientCallImpl.java:733) ~[grpc-core-1.71.0.jar:1.71.0]
	at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1StreamClosed.runInContext(ClientCallImpl.java:714) ~[grpc-core-1.71.0.jar:1.71.0]
	at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:37) ~[grpc-core-1.71.0.jar:1.71.0]
	at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:133) ~[grpc-core-1.71.0.jar:1.71.0]
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) ~[na:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) ~[na:na]
	at java.base/java.lang.Thread.run(Thread.java:1583) ~[na:na]
Caused by: io.netty.handler.ssl.NotSslRecordException: not an SSL/TLS record: 00001204000...