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 |
|---|---|
|
Only users with the specified role(s) can invoke the method. |
|
Any authenticated user can invoke the method. |
|
Any user (including anonymous, if the connection is allowed) can invoke the method. |
|
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 |
|---|---|---|
|
Unauthorized |
Authentication is required but was not provided or is invalid. |
|
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.
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
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-elytron-security-properties-file</artifactId>
</dependency>
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).