SORMAS Keycloak Integration
Authorization within the SORMAS is architected as a hybrid model, leveraging both Keycloak as an OIDC-compliant Identity Provider and SORMAS’s own domain-specific authorization logic. This integration is realized through a combination of OAuth2 Authorization Code Grant flows, JWT validation against dynamic JWKS endpoints, and J2EE security realm bridging.
For keycloak implementation and configuration details for Payara J2EE, see the Keycloak Integration Guides or the Payara documentation. Additionally the Keycloak documentation provides a comprehensive overview of the Keycloak OIDC Authentication, Keycloak Authorization and Keycloak Administration.
1. Keycloak (Identity Provider)
Role and Group Propagation:
Keycloak orchestrates principal authentication and propagates role/group assignments as OIDC claims within the JWT (typically in therealm_access.roles
and/orgroups
claims). These claims are subsequently consumed by the SORMAS application layer post-validation.Token Claims Semantics:
The JWTs issued by Keycloak encapsulate both identity and authorization context. SORMAS (J2EE) parses these claims after cryptographic verification, extracting role, group, and custom attributes for downstream authorization decisions.
2. SORMAS Application-Level Authorization with Keycloak
Authentication and initial role assignment are handled by Keycloak.
Fine-grained authorization and permission checks are enforced by the SORMAS application, based on its own user rights and business rules.
SORMAS relies on Keycloak for authorization; it uses its own roles and enforces its own permission model internally.
Hybrid Role Mapping:
While Keycloak is authoritative for authentication and initial role assertion, SORMAS overlays its own RBAC (Role-Based Access Control) and ABAC (Attribute-Based Access Control) logic. This is implemented via theUserRoleFacade
andUserRight
constructs, which are mapped from, but not limited to, the OIDC claims.Fine-Grained Policy Enforcement:
SORMAS enforces contextual permission checks at the service and resource layer, via EJB interceptors and REST filters (e.g.,SormasRestUserRightFilter
). These checks are decoupled from Keycloak’s coarse-grained role assertions and are evaluated against the application’s internal user rights matrix. Classes likeSormasRestUserRightFilter
and EJB authorization tests enforce these checks at the REST API and service layer. Additionally there are also checks in the UI layer and contextual presentation of features based on user rights and roles.User Rights:
SORMAS maintains a set of user rights and permissions (seeUserRoleFacade
andUserRight
). These are mapped to application features and are checked in the backend code before allowing access to specific resources or actions.Security Realm Bridging:
The integration leverages J2EE security realms, with the OIDC principal being mapped to the SORMAS domain user via a customIdentityStore
implementation. This enables seamless propagation of the security context across the application tier.
Authorization Aspect | Managed by Keycloak | Managed by SORMAS Application |
---|---|---|
Authentication | Yes | No |
Role Assignment | Partial | Yes |
Fine-grained Permissions | No | Yes |
Business Logic Checks | No | Yes |
User Rights | No | Yes |
3. Keycloak Integration
SORMAS is integrated with Keycloak as an identity provider.
The configuration files and code references show extensive use of OpenID Connect and OAuth2:
Keycloak realm and client configuration uses
"protocol": "openid-connect"
(SORMAS.json
).The UI and backend use classes like
OpenIdAuthenticationMechanism
,OpenIdContext
, andSormasOpenIdIdentityStore
(MultiAuthenticationMechanism.java
).REST API supports OIDC/OAuth2/Bearer authentication (
sormas-rest/README.md
).OAuth2 client credentials can be used if an integration with external clients is required.
3.1. JWT Validation
JWTs are verified against the JWKS endpoint provided by Keycloak. Classes such as OpenIdAuthenticationMechanism
and SormasOpenIdIdentityStore
are responsible for handling OIDC authentication. These typically:
Retrieve the JWKS from the Keycloak server (usually at
https://<keycloak-server>/auth/realms/<realm>/.well-known/openid-configuration
→jwks_uri
).Use the public keys from the JWKS to verify the JWT signature.
Validate token claims (issuer, audience, expiration, etc.).
3.2. Keycloak specific implementation with payara in SORMAS
Specific implementation of Keycloak with Payara in SORMAS is using the Keycloak Servlet Filter Adapter and custom authentication mechanisms :
Keycloak adapter libraries are included in the deployment.
KeycloakFilter is registered and enabled in Payara for REST endpoints.
Custom config resolver loads OIDC settings for the filter.
JWTs are validated against Keycloak’s JWKS endpoint.
User roles are assigned and checked by SORMAS based on database values.
Keycloak Adapter Dependencies Payara includes the Keycloak adapter libraries as dependencies:
<!-- Example from sormas-rest/pom.xml -->
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-servlet-filter-adapter</artifactId>
<!-- ... -->
</dependency>
Keycloak Filter Registration The Keycloak filter is registered in the REST API configuration:
// ...existing code...
if (authenticationProvider.equalsIgnoreCase(AuthProvider.KEYCLOAK)) {
FilterRegistration.Dynamic filterRegistration = ctx.addFilter("KeycloakFilter", KeycloakFilter.class);
// ...filter config...
logger.debug("Keycloak filter enabled");
} else {
logger.debug("Keycloak filter disabled");
}
// ...existing code...
Keycloak Configuration Resolver The Keycloak configuration is loaded using a custom resolver:
public class KeycloakConfigResolver implements org.keycloak.adapters.KeycloakConfigResolver {
@Override
public KeycloakDeployment resolve(HttpFacade.Request facade) {
// Loads configuration from sormas.rest.security.oidc.json
return KeycloakDeploymentBuilder.build(new ByteArrayInputStream(oidcJson.get().getBytes()));
}
}
Authentication Mechanism The authentication mechanism uses the Keycloak filter and identity store:
KeycloakHttpAuthenticationMechanism
andKeycloakIdentityStore
handle authentication and user mapping.The filter validates JWTs using Keycloak’s public keys (JWKS endpoint).
OIDC JSON Configuration Payara is pointed to a Keycloak OIDC JSON config file (e.g., sormas.rest.security.oidc.json), which contains:
Realm
Auth server URL
Client ID/secret
"protocol"
:"openid-connect"
3.3 Keycloak SORMAS authentication flow
Keycloak acts as an OpenID Connect (OIDC) Identity Provider (IdP), while the Sormas (J2EE) application delegates authentication to Keycloak using the Authorization Code Flow (https://auth0.com/docs/get-started/authentication-and-authorization-flow/authorization-code-flow).
Here’s how it works:
Step-by-Step Explanation
User Requests Protected Resource
The user attempts to access a secured endpoint in the J2EE application (e.g., /secured).
App Redirects to Keycloak
SORMAS (J2EE) detects the request is unauthenticated and redirects the user to Keycloak’s authorization endpoint:
HTTP 302 → Location: https://..../keycloak/realms/SORMAS/login-actions/authenticate?session_code=...&execution=...&client_id=sormas-ui&tab_id=...
User Authenticates with Keycloak
The user logs in via Keycloak’s login page (credentials, social login, etc.).
Keycloak Issues Authorization Code
Keycloak validates credentials and redirects the user back to the app with an authorization code:
HTTP 302 → Location: http://.../sormas-ui/Callback?state=...&session_state=...&code=...
App Exchanges Code for Tokens
The application exchanges the authorization code for tokens via a back-channel POST to the /protocol/openid-connect/token endpoint, authenticating with client credential.
The SORMAS app (J2ee) sends the authorization code to Keycloak’s token endpoint to request tokens:
POST /realms/SORMAS/protocol/openid-connect/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=...&client_id=sormas-ui&client_secret=...&redirect_uri=...
Keycloak Returns Tokens
Keycloak responds with:
ID Token (JWT with user identity claims).
Access Token (JWT for authorizing API requests).
Refresh Token (optional, for renewing access tokens).
App Validates Tokens
SORMAS (J2EE) verifies the token signature using Keycloak’s public key (from jwks_uri).
Checks token expiration, audience (client_id), and issuer (iss).
Access Granted
The app grants access to the secured resource if roles/permissions in the token match the web.xml constraints.
3.4 Keycloak user synchronization
User provisioning and synchronization between SORMAS and Keycloak is not automatic but is orchestrated via a UI-triggered, backend-mediated process. The synchronization process is done in the following steps:
User Initiates Sync from the UI
In the SORMAS UI, the user navigates to the user management section and clicks the "Sync Users" button.
This triggers the creation of a
UserSyncHandler
, which starts the sync process in a background thread.
Sync Handler Loops Over Users
UserSyncHandler retrieves all user UUIDs from the SORMAS database.
For each user, it calls
UserFacade
syncUser
method.UserFacade
retrieves the user entity from the database.It creates a UserUpdateEvent and fires it.
The result is wrapped in a result object (success/error).
Keycloak Integration
The
UserUpdateEvent
is observed byKeycloakService
:If the user exists in Keycloak, it updates the user’s information.
If the user does not exist, it creates a new user in Keycloak.
User attributes (username, email, roles, etc.) are mapped from SORMAS to Keycloak.
Special roles (like statistics access) are assigned as needed.
If the user has an email, activation or password reset emails may be sent.
Bulk Sync Option
There is also a bulk sync option managed by
UserController
:UserFacade
syncUsersFromAuthenticationProvider
method triggers a sync for all users.This method is called from the UI layer to trigger a bulk synchronization of all users from SORMAS to Keycloak.
4. SORMAS Keycloak & Payara Configuration Guide
This guide describes how the configuration of SORMAS Keycloak as the OpenID Connect (OIDC) Identity Provider is achieved, and how the integration is done with the Payara application server. It covers Keycloak realm/client setup, Docker deployment, Payara OIDC configuration, and environment-specific adjustments.
4.1. Keycloak Setup
4.1.1. Deploy Keycloak with Docker
Currently there is an existing docker setup using the provided docker-compose.yml
to deploy Keycloak and its PostgreSQL database:
cd sormas-base/setup/keycloak
docker compose up -d
The Keycloak docker image is provided as a convenience, it is not the latest version. The latest version of Keycloak can be found on the Keycloak Docker Hub page although the specific configuration will be different.
Environment variables (set in the .env file or export before running):
KEYCLOAK_PORT, KEYCLOAK_HOST
KEYCLOAK_ADMIN_USER, KEYCLOAK_ADMIN_PASSWORD
KEYCLOAK_DB_USER, KEYCLOAK_DB_PASSWORD, KEYCLOAK_DB_NAME
KEYCLOAK_SORMAS_UI_SECRET, KEYCLOAK_SORMAS_REST_SECRET, KEYCLOAK_SORMAS_BACKEND_SECRET
SORMAS_SERVER_URL
4.1.2. Import the SORMAS Realm
Import the preconfigured SORMAS realm using the SORMAS.json
file:
Access the Keycloak admin console (e.g., http://.../keycloak).
Log in as the admin user.
Go to Add Realm -> Import, and select sormas-base/setup/keycloak/SORMAS.json.
Review and confirm the import.
Key points from SORMAS.json
:
Defines the SORMAS realm, password policies, OTP, and required clients (sormas-ui, sormas-rest, sormas-backend, sormas-app, sormas-stats).
Sets up roles, protocol mappers, and client secrets.
4.1.3. Configure Clients
sormas-ui: For the web UI (confidential client, OIDC, secret required).
sormas-rest: For REST API access (confidential client, OIDC, secret required).
sormas-backend: For backend integration (confidential client, OIDC, secret required).
sormas-app: For the Android app.
sormas-stats: For the stats module.
Secrets for these clients must match the values in your environment and Payara configuration.
4.1.4. Adjust Realm Settings
Duplicate emails: Allowed.
Login with email: Disabled.
Password policy: Minimum 12 chars, upper/lower/digit/special.
OTP: Supported (enable in Keycloak if required).
Forgot password: Enabled.
Email verification: Optional, configure SMTP if needed.
4.2. Payara Configuration
4.2.1. Set OIDC Properties
Payara must be configured to use Keycloak as its OIDC provider. This can be done via asadmin commands, or through setup scripts (server-setup.sh, keycloak-setup.sh).
Example :
asadmin set-config-property --propertyName=payara.security.openid.clientId --propertyValue=sormas-ui --source=domain
asadmin set-config-property --propertyName=payara.security.openid.clientSecret --propertyValue=${KEYCLOAK_SORMAS_UI_SECRET} --source=domain
asadmin set-config-property --propertyName=payara.security.openid.scope --propertyValue=openid --source=domain
asadmin set-config-property --propertyName=payara.security.openid.providerURI --propertyValue=${KEYCLOAK_INTERNAL_URL}/realms/SORMAS --source=domain
asadmin set-config-property --propertyName=payara.security.openid.provider.notify.logout --propertyValue=true --source=domain
asadmin set-config-property --propertyName=payara.security.openid.logout.redirectURI --propertyValue=${KEYCLOAK_SORMAS_REDIRECT_URL} --source=domain
For REST and backend modules:
asadmin set-config-property --propertyName=sormas.rest.security.oidc.json --propertyValue='{"realm":"SORMAS","auth-server-url":"${KEYCLOAK_INTERNAL_URL}","ssl-required":"external","resource":"sormas-rest","credentials":{"secret":"${KEYCLOAK_SORMAS_REST_SECRET}"},"confidential-port":0,"principal-attribute":"preferred_username","enable-basic-auth":true}' --source=domain
asadmin set-config-property --propertyName=sormas.backend.security.oidc.json --propertyValue='{"realm":"SORMAS","auth-server-url":"${KEYCLOAK_INTERNAL_URL}","ssl-required":"external","resource":"sormas-backend","credentials":{"secret":"${KEYCLOAK_SORMAS_BACKEND_SECRET}"},"confidential-port":0}' --source=domain
4.2.2. Enable Keycloak Authentication in SORMAS
In the sormas.properties file, the following value is set:
authentication.provider=KEYCLOAK