Common Concepts
API Architecture
The OMN REST APIs are REST over HTTP-like APIs that follow the REST architectural style and guiding constraints. Clients can communicate with the APIs via HTTPS. The APIs support JSON and XML as request and response format.
REST Style and guiding constraints
Client-Server
The user interface (client) concerns are separated from the server (OMN API) concerns (separation of concerns), which means both components can evolve independently.
Statelessness
Servers don’t maintain client state, clients manage their own application state. The client’s requests to the server contain all the information required to process them.
Cacheable
Responses as marked as cacheable or not. Systems and clients can cache responses when convenient to improve performance. They also dispose of non-cacheable information, so no client uses stale data.
Layered system
Clients cannot see beyond server layer. For example if a proxy or load balancer is placed between the client and server. This confined scope allows OMN server to easily add load-balancers and proxies to improve authentication security or performance.
Uniform interface
OMN REST services provide data as resources, with a consistent namespace.
-
The resources requested are identifiable and separate from the representations sent to the client.
-
Resources can be manipulated by the client via the representation they receive because it contains enough information to do so.
-
Self-describing messages returned to the client contain enough information to describe the way the client processes them.
-
Hypertext/Hypermedia, i.e. after accessing a resource, the client should be able to use hyperlinks to find all currently available executable actions.
Supported Request and Response Formats
OMN API supports JSON and XML as request or response format.
JSON requests are supported in UTF-8 and are the default.
XML requests are supported in UTF-8 and UTF-16. XML responses are provided in UTF-8.
Use the HTTP ACCEPT header to specify either JSON or XML.
Cross-Origin Resource Sharing (CORS)
Problem: Current web browsers enforce that clients can only make requests to a resource whose origin matches the client’s URL. The client URL protocol, port, and hostname should all match the requested server. So how can clients use resources from external 3rd parties (for example from external APIs)?
Cross-Origin Resource Sharing (CORS) is a mechanism for integrating applications. It allows client web applications to request resources from origins other than their own (another domain outside the domain from which the first resource was served). This enables client applications to reference 3rd party API resources.
HTTP
HTTP which stands for HyperText Transfer Protocol (HTTP) is a standard (RFC 9110 HTTP Semantics) communication protocol for interactions on the internet.
It provides a uniform interface for interacting with a resource by sending messages that manipulate or transfer representations, regardless of its type, nature, or implementation. Each message is either a request or a response.
A client constructs request messages that communicate its intentions and routes those messages toward an identified origin server. A server listens for requests, parses each message received, interprets the message semantics in relation to the identified target resource, and responds to that request with one or more response messages. The client examines received responses to see if its intentions were carried out, determining what to do next based on the status codes and content received.
The HTTP specification defines a number of standardized, not resource-specific request methods that are commonly used in HTTP to indicate the purpose for which the client makes a request.
Request method examples: GET, POST, PUT and further (cp. HTTP methods).
Request structure
An HTTP request (client to server communication) mainly consists of the following parts:
| Part | Description |
|---|---|
Uniform Resource Identifier (URI) are used to identify the target of an HTTP request which is called a resource. HTTP does not limit the nature of a resource. |
|
request method which indicates the purpose for which the client has made this request. Examples: |
|
(aka headers) are sent or received before the content are referred to as "header fields". The "header section" of a message consists of a sequence of header field lines. Each header field might modify or extend message semantics, describe the sender, define the content, or provide additional context. |
|
represents the content of a request message which will be interpreted by the server-side (API side). It depends on the selected HTTP method if a request body is supported or not. |
|
The status code of a response is a three-digit integer code that describes the result of the request and the semantics of the response, including whether the request was successful and what content is enclosed (if any). Examples: 200=OK,400=Bad Request, 500=Internal Server Error |
|
represents the content of the response message which will be interpreted/displayed by the client-side. It depends on the selected HTTP method if a request body is supported or not. |
REST over HTTP
REST stands for Representational State Transfer which is an architectural style for distributed hypermedia systems and defines principles a service have to follow to be RESTful.
A stateless client-server protocol is used to implement the REST paradigm. The main application layer protocols used are HTTP and HTTPS. That is why it is also known as REST over HTTP.
Security
Access
To access the OMN REST API you can use basically any programming language that has support for the HTTP protocol. You can even use HTTP clients or command line tools like curl to process API requests.
Receive Tokens
Tokens can be generated on user level. You can generate access and offline tokens.
To receive a Token, please follow the steps described here: User Settings | Tokens
Keycloak Tokens
As an OAuth 2.0 server, Keycloak supports all standard authentication flows defined in the OIDC protocol.
Each Keycloak server provides a well-known URL for its OIDC configuration details:
https://<OMN_SERVER>/auth/realms/OMN/.well-known/openid-configuration
In OMN, we use the Authorization Code flow to authenticate users and authorize API access. This is the most secure option and is recommended by OIDC, but it is browser-based and somewhat complex to implement. To simplify using this flow for OMN API calls, we provide a special Token Management feature that allows users to obtain Access Tokens and Offline Tokens directly from the OMN UI with minimal effort.
For more details on the Authorization Code flow concept, please refer to the following documentation:
|
Keycloak is a standard OAuth 2.0 server, and users can leverage other authentication flows to manage tokens.
However, these flows are not covered in our documentation, nor are they officially supported by our team,
as only the Authorization Code flow has been fully tested with OMN’s permission logic. For details on alternative flows, please refer to the Keycloak documentation. All needed OIDC configuration could be obtained from the above-mentioned well-known URL. |
The Tokens management is available in User Settings | Tokens.
There are two types of tokens available in the OMN UI for API access. Both are issued by the Keycloak server for
the omn-ui client and inherit the exact permissions of the current user.
-
Offline Token - a special refresh Token which can be used without active user session
To understand the differences between Access Tokens and Refresh Tokens, please refer to the public documentation:
Access Token
This Token is issued by the Keycloak server and could be used to access the API directly. The Access Token expires after a set period, currently one hour.
| If the user logs out from OMN, the Access Token will be immediately invalidated and cannot be used anymore. Because this access Token is bound to the user session, it cannot be used without an active session. |
We recommend using this access token only for testing purposes or short-term API access.
How to use the Access Token
To use the Access Token the user must put it in the Authorization header of the API request.
Example:
curl --location --request GET 'https://<OMN_SERVER>/api/v1/search/omn-search-version' \
--header 'Authorization: Bearer <ACCESS_TOKEN>'
To access the API long-term, especially in production environments, users should utilize offline tokens instead.
Offline Token
|
Keycloak 18 contains a bug that may cause incorrect behavior of offline sessions. Please verify the following settings in the Advanced Settings:
|
The offline token is a special refresh token that cannot be used to access the API directly, but can be exchanged for an access token.
Unlike standard tokens, the offline token:
-
Is not bound to an active user session
-
Remains valid even after logout
-
Is ideal for backend services and long-term API access
For details about Offline Token, please refer to the following documentation:
The Offline Token will be expired after a certain period of time if it is not used, currently 30 days. But users could expand this period by using the rotation logic.
| Implementing this token rotation logic is the responsibility of the client application. |
How to get the Offline Token
To obtain an offline token, the user can retrieve it directly through the OMN UI, which is based on the Authorization Code Flow.
In some cases, users may want to obtain the offline token programmatically without any UI interaction. In this scenario, they can use the Resource Owner Password Credentials (ROPC) Flow.
Example:
curl --location --request POST 'https://<OMN_SERVER>/auth/realms/OMN/protocol/openid-connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=omn-ui' \
--data-urlencode 'grant_type=password' \
--data-urlencode 'username=YOUR_USER_NAME' \
--data-urlencode 'password=YOUR_USER_PASSWORD' \
--data-urlencode 'scope=openid offline_access'
| Using the ROPC flow poses security risks because it requires handling the user’s password directly. This approach should only be used in trusted environments and is generally discouraged in favor of more secure flows. |
How to use the Offline Token
We provide here a cURL example demonstrating offline token usage for the simple use cases e.g. in postman for testing purpose.
Example:
curl --location --request POST 'https://<OMN_SERVER>/auth/realms/OMN/protocol/openid-connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=omn-ui' \
--data-urlencode 'grant_type=refresh_token' \
--data-urlencode 'refresh_token=OFFLINE_TOKEN' \
--data-urlencode 'scope=openid offline_access'
This endpoint allows users to obtain both a new access token and refresh token (offline token).
Key characteristics:
-
The access token has the same lifespan as the Access Token obtained directly from OMN UI and can be used directly for API calls
-
The offline session refreshes with a new 30-days lifespan
-
When option
Revoke refresh tokensis disabled (default Keycloak setting), the previous offline token remains valid, otherwise it becomes invalid and must be replaced with the new one
In production environments, users must implement rotation logic for both access tokens and offline tokens to maintain
secure, uninterrupted API access. And we recommend always replacing the offline token with the new one regardless of
the Revoke refresh tokens setting.
| Please reuse the access token as long as possible and do not request a new one every time, otherwise it could put excessive load on the server. If possible, use the third party libraries for implementing your rotation logic. |
Here is a demo about using the offline token with rotation logic in Java keycloak library (users can use any other third party libraries):
Example:
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.keycloak.OAuth2Constants;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.util.JsonSerialization;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* This class demonstrates how to use offline token to manage the Keycloak tokens.
*/
public class KeycloakTokenManager {
public static final int TOKEN_EXPIRATION_BUFFER_MINUTES = 5;
private String serverUrl;
private String clientId;
private String clientSecret;
private String offlineToken;
private String accessToken;
private AccessTokenResponse tokenResponse;
private ScheduledExecutorService scheduler;
public static void main(String[] args) {
KeycloakTokenManager tokenManager = new KeycloakTokenManager(
"https://<OMN_SERVER>/auth", "omn-ui", null, "<INITIAL_OFFLINE_TOKEN>");
// Example: rotate token in API calls
tokenManager.rotateToken();
}
/**
* @param serverUrl OMN Auth server URL
* @param clientId Client ID e.g. "omn-ui"
* @param clientSecret could be null if the client is not confidential
* @param offlineToken the initial offline token from OMN User Settings
*/
public KeycloakTokenManager(String serverUrl, String clientId, String clientSecret, String offlineToken) {
this.serverUrl = serverUrl;
this.clientId = clientId;
this.clientSecret = clientSecret;
this.offlineToken = offlineToken;
this.scheduler = Executors.newSingleThreadScheduledExecutor();
}
public void rotateToken() {
if (tokenResponse == null || isTokenExpired()) {
refreshTokens();
}
}
private synchronized void refreshTokens() {
try (CloseableHttpClient client = HttpClients.createDefault()) {
HttpPost post = new HttpPost(serverUrl + "/realms/OMN/protocol/openid-connect/token");
List<NameValuePair> params = new ArrayList<>();
params.add(new BasicNameValuePair(OAuth2Constants.GRANT_TYPE, OAuth2Constants.REFRESH_TOKEN));
params.add(new BasicNameValuePair(OAuth2Constants.REFRESH_TOKEN, offlineToken));
params.add(new BasicNameValuePair(OAuth2Constants.CLIENT_ID, clientId));
params.add(new BasicNameValuePair(OAuth2Constants.CLIENT_SECRET, clientSecret));
post.setEntity(new UrlEncodedFormEntity(params));
try (CloseableHttpResponse response = client.execute(post)) {
HttpEntity entity = response.getEntity();
if (entity != null) {
try (InputStream is = entity.getContent()) {
tokenResponse = JsonSerialization.readValue(is, AccessTokenResponse.class);
// Update offline token
offlineToken = tokenResponse.getRefreshToken();
// Update access token
accessToken = tokenResponse.getToken();
// Print the exp of the access token
System.out.println("New assess token expired at: " + new Date(getTokenExpiredAt() * 1000));
scheduleTokenRefresh();
}
}
}
} catch (IOException e) {
throw new RuntimeException("Failed to refresh token", e);
}
}
private boolean isTokenExpired() {
if (tokenResponse == null) return true;
// Get expiration time
long expirationTime = getTokenExpiredAt() * 1000;
long currentTime = System.currentTimeMillis();
// Consider token expired with a buffered time e.g. 5 minutes before actual expiration
return expirationTime - currentTime < TimeUnit.MINUTES.toMillis(TOKEN_EXPIRATION_BUFFER_MINUTES);
}
private Long getTokenExpiredAt() {
if (tokenResponse == null) return null;
// Parse the JWT without validation (we trust Keycloak)
String token = tokenResponse.getToken();
String[] parts = token.split("\\.");
if (parts.length < 2) return null;
try {
// Decode the JWT payload (second part)
String payload = new String(Base64.getUrlDecoder().decode(parts[1]));
Map<String, Object> claims = JsonSerialization.readValue(payload, Map.class);
// Convert seconds to milliseconds
return ((Number) claims.get("exp")).longValue();
} catch (Exception e) {
throw new RuntimeException("Failed to parse JWT", e);
}
}
private void scheduleTokenRefresh() {
if (tokenResponse == null) return;
// Calculate refresh time with buffer (e.g. 5 minutes before expiration)
long refreshDelay = (tokenResponse.getExpiresIn() - TimeUnit.MINUTES.toSeconds(TOKEN_EXPIRATION_BUFFER_MINUTES)) * 1000;
System.out.println("Next refresh in (ms): " + refreshDelay);
scheduler.schedule(() -> {
refreshTokens();
}, refreshDelay, TimeUnit.MILLISECONDS);
}
public void shutdown() {
scheduler.shutdown();
}
}
Maven dependencies of the above example:
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-admin-client</artifactId>
<version>18.0.2</version> <!-- same version as keycloak server-->
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-admin-client</artifactId>
<version>18.0.2</version> <!-- same version as keycloak server-->
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>18.0.2</version> <!-- same version as keycloak server-->
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.13.2</version>
</dependency>
</dependencies>
| Please always keep the libraries having the same version as the Keycloak server. Currently, we are using Keycloak 18.0.2. |
Authentication
The OMN REST APIs use JSON Web Tokens (JWT) to secure the access. You have to provide a valid JWT in the authorization header to access the OMN API resources. If the API requires also authorization, related information must also be sent via this token.
To debug JWTs you can use jwt.io.