REST DataStore with External Authentication
This guide shows how to use the Jmix REST DataStore together with external authentication, using Keycloak as an OpenID Connect (OIDC) identity provider.
In distributed systems, it’s common for applications to authenticate users centrally but share data and logic between services. With Jmix, you can build secure applications where user data and access control are managed externally, while services communicate through REST APIs and forward user tokens for authentication.
You’ll learn how to:
-
Set up a Keycloak server for managing users, roles, and tokens.
-
Configure Jmix applications to authenticate users via Keycloak.
-
Implement user synchronization between Keycloak and Jmix.
-
Use the REST DataStore to access data from another application on behalf of the logged-in user.
The guide covers two architectural patterns:
-
Integrated Applications: Both the Client and Service applications have UIs and run independently, communicating through REST with token-based authentication.
-
Separate Tiers: The UI-only Frontend application retrieves data from a Backend service via REST, with all authentication handled through Keycloak.
By the end of this guide, you’ll have a working example of both scenarios and understand the key concepts behind integrating REST DataStore with external authentication in a secure and maintainable way.
Prerequisites
Get Sample Project
-
Setup the development environment.
-
Clone the sample project and switch to
release_2_5
branch:git clone https://github.com/jmix-framework/jmix-restds-oidc-sample cd jmix-restds-oidc-sample git checkout release_2_5
Set Up Keycloak
-
Run Keycloak in a Docker container:
docker run -p 8180:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin --name=keycloak quay.io/keycloak/keycloak:26.1 start-dev
-
Log in to the Keycloak admin console and create a new realm called
jmix-restds-oidc-sample
. -
Create the following clients in this realm:
-
integrated-apps-client
-
Capability config tab:
-
Client authentication: On
-
-
Login settings tab:
-
Valid redirect URIs:
http://localhost:8080/*
,http://localhost:8081/*
-
Web origins:
+
-
-
-
integrated-apps-service
-
Capability config tab:
-
Client authentication: On
-
-
Login settings tab:
-
Valid redirect URIs:
http://localhost:8080/*
,http://localhost:8081/*
-
Web origins:
+
-
-
-
separate-tiers-frontend
-
Capability config tab:
-
Client authentication: On
-
-
Login settings tab:
-
Valid redirect URIs:
http://localhost:8080/*
-
Web origins:
+
-
-
-
-
For each client, switch to the Client scopes tab and open the
[client-name]-dedicated
scope. Add predefined mapper forrealm roles
. Open the createdrealm roles
mapper and set the following values:-
Token Claim Name:
roles
-
Add to userinfo: On
-
-
Create two realm roles:
system-full-access
andemployee
. -
Create two users:
alice
andbob
. Fill in the Email, First name and Last name attributes. Set user passwords on the Credentials tab. -
On the Role mapping tab, assign
system-full-access
role toalice
andemployee
role tobob
. Use Filter by realm roles to find the roles.
Integrated Applications Example
In this example, the distributed system consists of two applications: Client and Service.
Both applications have their own databases and UI, and use the Jmix OpenID Connect add-on to enable user authentication through Keycloak. The Client application accesses customer data through the REST DataStore.
The Service application includes the Customer
JPA entity and customer management views. The Client application has a corresponding Customer
DTO entity and related views.
When the Client’s REST DataStore makes a request to the Service’s REST API, it includes the authorization token of the current user obtained from Keycloak during login. Thus, when executing REST requests, the Service code acts on behalf of the user who is logged into the Client.
Each application has two roles: system-full-access
and employee
.
The employee
role in Service application provides access only to the Customer
entity and related views. In the Client application, this role also enables managing the Order
entity.
According to the Keycloak setup, alice
will be granted the system-full-access
role and bob
will be granted the employee
role.
Example in Action
-
Open the root project in IntelliJ IDEA with the Jmix Studio plugin installed.
Launch the
integrated-apps-client
andintegrated-apps-service
applications using their run/debug configurations.The applications are configured to run on different ports: Client on 8080, Service on 8081.
-
Open the Client application by navigating to http://localhost:8080 in your web browser. You will be redirected to the Keycloak login page.
Log in as
bob
with the password that you have set in Keycloak. You will be able to manage customers inCustomer (DTO)
views.Log in as
alice
. Open the list of users and make sure it contains bothbob
andalice
users with data replicated from Keycloak. -
Open the Service application by navigating to http://localhost:8081 in your web browser. You will be redirected to the Keycloak login page.
Log in as
alice
. You will be able to manage customers and see the list of users with data replicated from Keycloak.
Service Application Details
-
The Service application includes the REST API and OpenID Connect add-ons:
integrated-apps/service-app/build.gradleimplementation 'io.jmix.rest:jmix-rest-starter' implementation 'io.jmix.oidc:jmix-oidc-starter'
-
To run on a specific port and prevent cookie conflicts, the Service application requires the following properties:
integrated-apps/service-app/src/main/resources/application.propertiesserver.port=8081 server.servlet.session.cookie.name=SERVICEAPP_JSESSIONID
-
Spring Security OAuth2 properties provide integration with Keycloak for authenticating users in the UI and validating tokens sent by the Client in REST API requests:
integrated-apps/service-app/src/main/resources/application.propertiesspring.security.oauth2.client.registration.keycloak.client-id=integrated-apps-service spring.security.oauth2.client.registration.keycloak.client-secret=uZXD0xfHn594ncTKQR5SHhTBFTKGmR8T spring.security.oauth2.client.registration.keycloak.scope=openid,profile,email,offline_access spring.security.oauth2.client.provider.keycloak.issuer-uri=http://localhost:8180/realms/jmix-restds-oidc-sample spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8180/realms/jmix-restds-oidc-sample
-
The REST API endpoints are secured by specifying the following property:
integrated-apps/service-app/src/main/resources/application.propertiesjmix.resource-server.authenticated-url-patterns=/rest/**
-
In order to bypass the Spring Boot’s default login page and go directly to Keycloak on login, the application includes the following property and customized security configuration:
integrated-apps/service-app/src/main/resources/application.propertiesjmix.oidc.use-default-ui-configuration=false
integrated-apps/service-app/src/main/java/com/company/serviceapp/security/AppSecurityConfiguration.javapackage com.company.serviceapp.security; import io.jmix.oidc.OidcVaadinWebSecurity; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; @Configuration public class AppSecurityConfiguration extends OidcVaadinWebSecurity { @Override protected void configure(HttpSecurity http) throws Exception { super.configure(http); http.oauth2Login(oauth2Login -> oauth2Login .loginPage("/oauth2/authorization/keycloak") .defaultSuccessUrl("/", true)); } }
-
For compatibility with Jmix OIDC add-on, the
User
entity extends theJmixOidcUserEntity
class:integrated-apps/service-app/src/main/java/com/company/serviceapp/entity/User.java@JmixEntity @Entity @Table(name = "USER_", indexes = { @Index(name = "IDX_USER__ON_USERNAME", columnList = "USERNAME", unique = true) }) public class User extends JmixOidcUserEntity implements HasTimeZone {
-
AppUserMapper
bean performs synchronization ofUser
entity instances with the user information from Keycloak:integrated-apps/service-app/src/main/java/com/company/serviceapp/security/AppUserMapper.javapackage com.company.serviceapp.security; import com.company.serviceapp.entity.User; import io.jmix.core.UnconstrainedDataManager; import io.jmix.core.security.UserRepository; import io.jmix.oidc.claimsmapper.ClaimsRolesMapper; import io.jmix.oidc.usermapper.SynchronizingOidcUserMapper; import io.jmix.security.role.RoleGrantedAuthorityUtils; import org.springframework.security.oauth2.core.oidc.user.OidcUser; import org.springframework.stereotype.Component; @Component public class AppUserMapper extends SynchronizingOidcUserMapper<User> { public AppUserMapper(UnconstrainedDataManager dataManager, UserRepository userRepository, ClaimsRolesMapper claimsRolesMapper, RoleGrantedAuthorityUtils roleGrantedAuthorityUtils) { super(dataManager, userRepository, claimsRolesMapper, roleGrantedAuthorityUtils); } @Override protected Class<User> getApplicationUserClass() { return User.class; } @Override protected void populateUserAttributes(OidcUser oidcUser, User jmixUser) { jmixUser.setUsername(getOidcUserUsername(oidcUser)); jmixUser.setFirstName(oidcUser.getGivenName()); jmixUser.setLastName(oidcUser.getFamilyName()); jmixUser.setEmail(oidcUser.getEmail()); } @Override protected String getOidcUserUsername(OidcUser oidcUser) { return oidcUser.getPreferredUsername(); } }
Client Application Details
-
The Client application includes the REST DataStore and OpenID Connect add-ons:
integrated-apps/client-app/build.gradleimplementation 'io.jmix.restds:jmix-restds-starter' implementation 'io.jmix.oidc:jmix-oidc-starter'
-
Spring Security OAuth2 properties provide integration with Keycloak for authenticating users in the UI:
integrated-apps/client-app/src/main/resources/application.propertiesspring.security.oauth2.client.registration.keycloak.client-id=integrated-apps-client spring.security.oauth2.client.registration.keycloak.client-secret=DmkKz9syYW5OAptnYCdXKnGTD5kDoMRm spring.security.oauth2.client.registration.keycloak.scope=openid,profile,email,offline_access spring.security.oauth2.client.provider.keycloak.issuer-uri=http://localhost:8180/realms/jmix-restds-oidc-sample spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8180/realms/jmix-restds-oidc-sample
-
The Client application includes the same configuration as that of the Service application described in items 5, 6, and 7 of the previous section:
-
jmix.oidc.use-default-ui-configuration=false
property andAppSecurityConfiguration
class that configures OAuth2 login page. -
User
entity extendsJmixOidcUserEntity
for compatibility with OIDC add-on. -
AppUserMapper
class that performs synchronization ofUser
entity instances with the user information from Keycloak.
-
-
The
serviceapp
REST DataStore is configured as follows:integrated-apps/client-app/src/main/resources/application.propertiesjmix.core.additional-stores=serviceapp jmix.core.store-descriptor-serviceapp=restds_RestDataStoreDescriptor serviceapp.baseUrl=http://localhost:8081
-
To use the OAuth2 token of the current user when making requests to the Service’s REST API, the Client application defines the following bean that implements the
RestAuthenticator
interface:integrated-apps/client-app/src/main/java/com/company/clientapp/security/RestOidcAuthenticator.javapackage com.company.clientapp.security; import io.jmix.restds.impl.RestAuthenticator; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; import org.springframework.http.HttpRequest; import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest; import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager; import org.springframework.stereotype.Component; import java.io.IOException; @Component @Scope(BeanDefinition.SCOPE_PROTOTYPE) public class RestOidcAuthenticator implements RestAuthenticator { @Autowired private OAuth2AuthorizedClientManager authorizedClientManager; @Override public void setDataStoreName(String name) { } @Override public ClientHttpRequestInterceptor getAuthenticationInterceptor() { return new AuthenticatingClientHttpRequestInterceptor(); } private String getAccessToken() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication == null) { throw new IllegalStateException("Cannot get access token: Authentication object is null"); } OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("keycloak") .principal(authentication) .build(); OAuth2AuthorizedClient authorizedClient = authorizedClientManager.authorize(authorizeRequest); if (authorizedClient == null) { throw new IllegalStateException("Cannot authorize " + authorizeRequest); } return authorizedClient.getAccessToken().getTokenValue(); } private class AuthenticatingClientHttpRequestInterceptor implements ClientHttpRequestInterceptor { @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { request.getHeaders().setBearerAuth(getAccessToken()); return execution.execute(request, body); } } }
This bean’s name is specified in the
[restds-name].authenticator
property:integrated-apps/client-app/src/main/resources/application.propertiesserviceapp.authenticator=restOidcAuthenticator
Separate Tiers Example
In this example, the distributed system comprises the Backend and Frontend applications.
The Backend application connects to the database and provides the REST API. The Frontend application features a user interface and retrieves data from the Backend via the REST DataStore.
Both applications use the Jmix OpenID Connect add-on for integration with Keycloak: the Frontend authenticates users in UI, the Backend validates tokens sent by the Client with REST requests.
The data models of the applications have the same structure; however, the Backend includes JPA entities, while the Frontend includes their corresponding DTO entities.
When the Frontend REST DataStore makes a request to the Backend REST API, it includes the authorization token of the current user obtained from Keycloak during login. Thus, when executing REST requests, the Backend code acts on behalf of the user who is logged into the Frontend.
Each application has two roles: system-full-access
and employee
. The employee
role provides access only to the Customer
entity and, in the Frontend application, to the corresponding views.
According to the Keycloak setup, alice
will be granted the system-full-access
role and bob
will be granted the employee
role.
Example in Action
-
Open the root project in IntelliJ IDEA with the Jmix Studio plugin installed.
Launch the
separate-tiers-backend
andseparate-tiers-frontend
applications using their run/debug configurations.The applications are configured to run on different ports: Frontend on 8080, Backend on 8081.
-
Open the Frontend application by navigating to http://localhost:8080 in your web browser. You will be redirected to the Keycloak login page.
Log in as
bob
with the password that you have set in Keycloak. You will be able to manage customers inCustomer
views.Log in as
alice
. Open the list of users and make sure it contains bothbob
andalice
users with data replicated from Keycloak.
Backend Application Details
-
The Backend application includes the REST API and OpenID Connect add-ons:
separate-tiers/backend-app/build.gradleimplementation 'io.jmix.rest:jmix-rest-starter' implementation 'io.jmix.oidc:jmix-oidc-starter' implementation 'io.jmix.flowui:jmix-flowui-data-starter'
While the Backend application doesn’t have a UI itself, it still needs the
jmix-flowui-data-starter
dependency to provide the database persistence for UI filter configurations and user settings. -
The Backend application runs on a non-standard port:
separate-tiers/backend-app/src/main/resources/application.propertiesserver.port=8081
-
The following Spring Security OAuth2 property provides validation of tokens sent by the Client in REST API requests:
separate-tiers/backend-app/src/main/resources/application.propertiesspring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8180/realms/jmix-restds-oidc-sample
-
The REST API endpoints are secured by specifying the following property:
separate-tiers/backend-app/src/main/resources/application.propertiesjmix.resource-server.authenticated-url-patterns=/rest/**
-
For compatibility with Jmix OIDC add-on, the
User
entity extends theJmixOidcUserEntity
class:separate-tiers/backend-app/src/main/java/com/company/backendapp/entity/User.java@JmixEntity @Entity @Table(name = "USER_", indexes = { @Index(name = "IDX_USER__ON_USERNAME", columnList = "USERNAME", unique = true) }) public class User extends JmixOidcUserEntity implements HasTimeZone {
-
BackendUserMapper
bean performs synchronization ofUser
entity instances with the user information from Keycloak:separate-tiers/backend-app/src/main/java/com/company/backendapp/security/AppUserMapper.javapackage com.company.backendapp.security; import com.company.backendapp.entity.User; import io.jmix.core.UnconstrainedDataManager; import io.jmix.core.security.UserRepository; import io.jmix.oidc.claimsmapper.ClaimsRolesMapper; import io.jmix.oidc.usermapper.SynchronizingOidcUserMapper; import io.jmix.security.role.RoleGrantedAuthorityUtils; import org.springframework.security.oauth2.core.oidc.user.OidcUser; import org.springframework.stereotype.Component; @Component public class BackendUserMapper extends SynchronizingOidcUserMapper<User> { public BackendUserMapper(UnconstrainedDataManager dataManager, UserRepository userRepository, ClaimsRolesMapper claimsRolesMapper, RoleGrantedAuthorityUtils roleGrantedAuthorityUtils) { super(dataManager, userRepository, claimsRolesMapper, roleGrantedAuthorityUtils); } @Override protected Class<User> getApplicationUserClass() { return User.class; } @Override protected void populateUserAttributes(OidcUser oidcUser, User jmixUser) { jmixUser.setUsername(getOidcUserUsername(oidcUser)); jmixUser.setFirstName(oidcUser.getGivenName()); jmixUser.setLastName(oidcUser.getFamilyName()); jmixUser.setEmail(oidcUser.getEmail()); } @Override protected String getOidcUserUsername(OidcUser oidcUser) { return oidcUser.getPreferredUsername(); } }
Frontend Application Details
-
The Client application includes the REST DataStore and OpenID Connect add-ons:
separate-tiers/frontend-app/build.gradleimplementation 'io.jmix.restds:jmix-restds-starter' implementation 'io.jmix.oidc:jmix-oidc-starter' implementation 'io.jmix.flowui:jmix-flowui-restds-starter'
The
jmix-flowui-restds-starter
dependency provides implementation of UI filter configuration and user settings persistence based on REST DataStore. -
Spring Security OAuth2 properties provide integration with Keycloak for authenticating users in the UI:
separate-tiers/frontend-app/src/main/resources/application.propertiesspring.security.oauth2.client.registration.keycloak.client-id=separate-tiers-frontend spring.security.oauth2.client.registration.keycloak.client-secret=vVDzrbhOzbbzlgx3TDhZvNcuauMemhVR spring.security.oauth2.client.registration.keycloak.scope=openid,profile,address,email,offline_access spring.security.oauth2.client.provider.keycloak.issuer-uri=http://localhost:8180/realms/jmix-restds-oidc-sample spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8180/realms/jmix-restds-oidc-sample
-
In order to bypass the Spring Boot’s default login page and go directly to Keycloak on login, the application includes the following property and customized security configuration:
separate-tiers/frontend-app/src/main/resources/application.propertiesjmix.oidc.use-default-ui-configuration=false
separate-tiers/frontend-app/src/main/java/com/company/frontendapp/security/AppSecurityConfiguration.javapackage com.company.frontendapp.security; import io.jmix.oidc.OidcVaadinWebSecurity; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; @Configuration public class AppSecurityConfiguration extends OidcVaadinWebSecurity { @Override protected void configure(HttpSecurity http) throws Exception { super.configure(http); http.oauth2Login(oauth2Login -> oauth2Login .loginPage("/oauth2/authorization/keycloak") .defaultSuccessUrl("/", true)); } }
-
For compatibility with Jmix OIDC add-on, the
User
DTO entity extends theDefaultJmixOidcUser
class:separate-tiers/frontend-app/src/main/java/com/company/frontendapp/entity/User.java@Store(name = "backend") @JmixEntity(annotatedPropertiesOnly = true) public class User extends DefaultJmixOidcUser implements HasTimeZone {
-
FrontendUserMapper
bean maps the user information from Keycloak to theUser
DTO entity:integrated-apps/service-app/src/main/java/com/company/serviceapp/security/AppUserMapper.javapackage com.company.frontendapp.security; import com.company.frontendapp.entity.User; import io.jmix.core.Metadata; import io.jmix.oidc.claimsmapper.ClaimsRolesMapper; import io.jmix.oidc.usermapper.BaseOidcUserMapper; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.oauth2.core.oidc.user.OidcUser; import org.springframework.stereotype.Component; import java.util.Collection; @Component public class FrontendUserMapper extends BaseOidcUserMapper<User> { private final Metadata metadata; private final ClaimsRolesMapper claimsRolesMapper; public FrontendUserMapper(Metadata metadata, ClaimsRolesMapper claimsRolesMapper) { this.metadata = metadata; this.claimsRolesMapper = claimsRolesMapper; } @Override protected String getOidcUserUsername(OidcUser oidcUser) { return oidcUser.getPreferredUsername(); } @Override protected User initJmixUser(OidcUser oidcUser) { return metadata.create(User.class); } @Override protected void populateUserAttributes(OidcUser oidcUser, User jmixUser) { jmixUser.setUsername(getOidcUserUsername(oidcUser)); jmixUser.setFirstName(oidcUser.getGivenName()); jmixUser.setLastName(oidcUser.getFamilyName()); jmixUser.setEmail(oidcUser.getEmail()); } @Override protected void populateUserAuthorities(OidcUser oidcUser, User jmixUser) { Collection<? extends GrantedAuthority> authorities = claimsRolesMapper.toGrantedAuthorities(oidcUser.getClaims()); jmixUser.setAuthorities(authorities); } }
Notice that this mapper extends
BaseOidcUserMapper
and does not persist the user data to any storage. Instead, the Frontend relies on synchronization of users by theBackendUserMapper
of the Backend application. -
The
backend
REST DataStore is configured as follows:separate-tiers/frontend-app/src/main/resources/application.propertiesjmix.core.additional-stores=backend jmix.core.store-descriptor-backend=restds_RestDataStoreDescriptor backend.baseUrl=http://localhost:8081 jmix.restds.ui-config-store=backend
The
jmix.restds.ui-config-store property
defines the REST data store that will be used for UI filter configuration and user settings and persistence. -
To use the OAuth2 token of the current user when making requests to the Backend REST API, the Frontend application defines the following bean that implements the
RestAuthenticator
interface:separate-tiers/frontend-app/src/main/java/com/company/frontendapp/security/RestOidcAuthenticator.javapackage com.company.frontendapp.security; import io.jmix.restds.impl.RestAuthenticator; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; import org.springframework.http.HttpRequest; import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest; import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager; import org.springframework.stereotype.Component; import java.io.IOException; @Component @Scope(BeanDefinition.SCOPE_PROTOTYPE) public class RestOidcAuthenticator implements RestAuthenticator { @Autowired private OAuth2AuthorizedClientManager authorizedClientManager; @Override public void setDataStoreName(String name) { } @Override public ClientHttpRequestInterceptor getAuthenticationInterceptor() { return new AuthenticatingClientHttpRequestInterceptor(); } private String getAccessToken() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication == null) { throw new IllegalStateException("Cannot get access token: Authentication object is null"); } OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("keycloak") .principal(authentication) .build(); OAuth2AuthorizedClient authorizedClient = authorizedClientManager.authorize(authorizeRequest); if (authorizedClient == null) { throw new IllegalStateException("Cannot authorize " + authorizeRequest); } return authorizedClient.getAccessToken().getTokenValue(); } private class AuthenticatingClientHttpRequestInterceptor implements ClientHttpRequestInterceptor { @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { request.getHeaders().setBearerAuth(getAccessToken()); return execution.execute(request, body); } } }
This bean’s name is specified in the
[restds-name].authenticator
property:separate-tiers/frontend-app/src/main/resources/application.propertiesbackend.authenticator=restOidcAuthenticator
Summary
This guide demonstrates how to use REST DataStore with external authentication in Jmix applications, leveraging Keycloak as the OpenID Connect provider. Two architectural patterns are covered:
-
Integrated Applications Example
-
Client and Service applications run independently, each with their own UI and database.
-
The Client accesses the Service’s REST API using REST DataStore, forwarding the user’s OAuth2 token for authentication.
-
Both applications use Jmix OIDC add-on for Keycloak integration, with role-based access control.
-
The Service validates incoming OAuth2 tokens and applies the authentication of the user logged into the Client when executing code.
-
-
Separate Tiers Example
-
A Frontend application (UI-only) retrieves data from a Backend (REST API + database) via REST DataStore.
-
The Frontend authenticates users via Keycloak and includes their token in REST requests.
-
The Backend validates OAuth2 tokens and applies the authentication of the user logged into the Client when executing code.
-
Key implementation details:
-
The REST DataStore configuration includes a custom
RestAuthenticator
to forward OAuth2 tokens. -
The security customization includes custom mappers based on classes from Jmix OIDC add-on for synchronizing user data from Keycloak.
By following this guide, developers can implement secure, distributed Jmix applications with external authentication and REST-based data access.