Security

Quarkus JSON-RPC integrates with the standard Quarkus security framework. You can secure your endpoints using CDI security annotations on your @JsonRPCApi classes, or by applying HTTP auth policies to the WebSocket path via configuration. Both approaches are opt-in — endpoints are unsecured by default.

Security Annotations

Add standard Jakarta security annotations directly to your @JsonRPCApi classes and methods. This requires a Quarkus security extension on the classpath (e.g., quarkus-oidc, quarkus-elytron-security-properties-file).

Class-Level Security

Annotations on the class apply to all methods:

@JsonRPCApi
@RolesAllowed("admin")
public class AdminService {

    public String getSecretData() {
        return "top-secret"; (1)
    }
}
1 Only users with the admin role can call this method.

Method-Level Overrides

Method-level annotations override the class-level setting:

@JsonRPCApi
@RolesAllowed("admin")
public class AdminService {

    public String adminOnly() {
        return "admin-secret"; (1)
    }

    @PermitAll
    public String publicInfo() {
        return "public-info"; (2)
    }

    @RolesAllowed("user")
    public String userInfo() {
        return "user-info"; (3)
    }
}
1 Inherits @RolesAllowed("admin") from the class.
2 @PermitAll overrides — any authenticated user can call this.
3 @RolesAllowed("user") overrides — only users with the user role.

Supported Annotations

Annotation Effect

@RolesAllowed("role")

Only users with the specified role(s) can invoke the method.

@Authenticated

Any authenticated user can invoke the method.

@PermitAll

Any user (including anonymous, if the connection is allowed) can invoke the method.

@DenyAll

No user can invoke the method.

Error Responses

When a security check fails, the extension returns a JSON-RPC error response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32001,
    "message": "Method [AdminService#adminOnly] failed: ..."
  }
}
Code Name Meaning

-32000

Unauthorized

Authentication is required but was not provided or is invalid.

-32001

Forbidden

Authenticated but not authorized for the requested method.

HTTP Auth Policies

You can also secure the entire WebSocket endpoint at the HTTP level using standard Quarkus HTTP auth policies. This protects the WebSocket upgrade — unauthenticated users receive an HTTP 401 response before a WebSocket connection is established.

# Require authentication for the JSON-RPC endpoint
quarkus.http.auth.permission.json-rpc.paths=/quarkus/json-rpc
quarkus.http.auth.permission.json-rpc.policy=authenticated

This approach works with any Quarkus security mechanism (Basic auth, OIDC, JWT, etc.) and requires no code changes to your @JsonRPCApi classes.

Role-Based HTTP Policies

You can restrict access to specific roles at the HTTP level:

quarkus.http.auth.permission.json-rpc.paths=/quarkus/json-rpc
quarkus.http.auth.permission.json-rpc.policy=role-policy

quarkus.http.auth.policy.role-policy.roles-allowed=admin,operator

Combining Both Approaches

Annotations and HTTP policies can be combined:

  • HTTP auth policy — gates who can connect to the WebSocket.

  • Security annotations — gates who can invoke specific methods within a connection.

For example, require authentication to connect, then use @RolesAllowed to restrict individual operations:

quarkus.http.auth.permission.json-rpc.paths=/quarkus/json-rpc
quarkus.http.auth.permission.json-rpc.policy=authenticated
@JsonRPCApi
public class DataService {

    @RolesAllowed("admin")
    public String sensitiveOperation() { ... }

    public String regularOperation() { ... } (1)
}
1 Any authenticated user can call this (connection already requires authentication).

Example: Basic Auth Setup

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-elytron-security-properties-file</artifactId>
</dependency>
application.properties
quarkus.http.auth.basic=true
quarkus.security.users.embedded.enabled=true
quarkus.security.users.embedded.plain-text=true
quarkus.security.users.embedded.users.admin=secret
quarkus.security.users.embedded.roles.admin=admin

Connect with credentials from a Java or Node.js client (which supports custom headers directly):

// Node.js — custom headers are supported
const ws = new WebSocket('ws://localhost:8080/quarkus/json-rpc', {
    headers: { Authorization: 'Basic ' + btoa('admin:secret') }
});

Browser Authentication

JavaScript’s browser WebSocket API does not support custom headers like Authorization. The extension provides built-in support for the Quarkus Sec-WebSocket-Protocol encoding pattern, which encodes credentials into the sub-protocol header during the upgrade handshake. The server-side filter extracts these credentials and sets them as real HTTP headers before Quarkus authentication runs.

Using the Generated Proxy

The simplest approach — set global auth defaults and the generated proxy picks them up automatically:

import { JsonRPCClient } from '@quarkiverse/json-rpc';
import { AdminService } from '@quarkiverse/json-rpc-api';

// Static token
JsonRPCClient.configure({ token: 'Bearer my-jwt-token' });

// Or use a dynamic provider (sync or async)
JsonRPCClient.configure({
    tokenProvider: async () => {
        const token = await fetchAccessToken();
        return 'Bearer ' + token;
    }
});

// Calls are authenticated automatically
const result = await AdminService.adminOnly();

Using the Client Library Directly

import { JsonRPCClient } from '@quarkiverse/json-rpc';

const client = new JsonRPCClient({
    token: 'Basic ' + btoa('admin:secret')
});

const result = await client.call('AdminService#adminOnly');

Refreshing Tokens

Use updateToken() to update the token and reconnect with new credentials:

// Token refresh — disconnects and reconnects automatically
client.updateToken('Bearer ' + newAccessToken);

Setting the token property directly updates the stored token without reconnecting. The new token takes effect on the next connection (e.g., after a reconnect).