-
SZ-4a: Implement Authentication (OAuth2/OIDC login + JWT token support)
This sub-issue focuses on the authentication (AuthN) setup for the Sztab backend using Spring Security and Keycloak.
Scope
- Configure
oauth2Login()for Web UI (/ui/**) via Keycloak - Support JWT-based stateless authentication for API (
/api/**) using Keycloak-issued tokens - Integrate with Spring Security’s
SecurityFilterChain - Decode and validate JWT tokens in the backend using a custom
JwtAuthenticationFilter - Populate the authenticated user from the token (
Principal, roles, etc.) - Leave authorization (role-based access control) for follow-up task (SZ-4b)
Estimated Effort
Task Est. Time Configure Keycloak clients (UI + API) 0.5h Update application.ymlwith Keycloak OIDC config0.5h Implement SecurityConfig.javawith two filter chains1.5h Create JwtTokenProvider.java(token parser/validator)1h Create JwtAuthenticationFilter.java1h Test Web UI login flow (oauth2Login + session) 0.5h Test REST API access with JWT token (manual or Postman) 0.5h Add basic unit/integration tests for AuthN setup 1h Write usage notes or dev README 0.5h Total Estimate: 7 hours
- Configure
-
Design:
-
SecurityConfig.java • Two filter chains: • /ui/** → oauth2Login() (OIDC via Keycloak) • /api/** → JWT-based stateless security
-
application.yml (partial) • Keycloak client config for oauth2Login
-
JwtTokenProvider.java • Validates and parses tokens from the Authorization header • Uses Keycloak’s public keys (JWKS endpoint)
-
JwtAuthenticationFilter.java • Intercepts /api/** • Extracts and validates JWT • Populates SecurityContext
-
-
@kobit @rk I wonder about this task. I know it may be a little to late, but why/when we decided to user Keycloak (Identity and Access Manager, see https://www.keycloak.org/). I know it is often used, but if we would like to add Sztab to our Tygrys deployment then we would have to deploy Keycloak on each cluster.... I suppose Keycloack could then forward requests (authentication requests) over LDAP to Tigase XMPP Server (LDAP server), but I just wonder if that wouldn't be an overkill for us.
-
Andrzej, thanks for the note - I was going to directly reach out to you as well on the design choices I have made. I chose Keycloak not because it’s the only option, but because it gives us flexibility at a relatively low cost: I was looking not narrowly at our own use case alone, but also the use cases of open source users who look for enterprise capabilities in Sztab.
Keycloak handles a lot of the complexity that typically creeps into authentication:
- supports modern authentication flows out of the box: OAuth2, OIDC, SAML2, and PKCE (important for mobile clients).
- integrates with external identity providers like GitHub, Google, Facebook, corporate LDAPs, or even SAML2 IdPs, without any code changes on our side.
- manages JWT issuance, token expiry, revocation, 2FA, SSO, and user federation—all through configuration.
- operationally, it’s light: a single pod with a Postgres backend and well-documented Helm charts.
Here is a visual:

In other words, it gives us a clean boundary between identity management and our application logic. That’s especially helpful if Sztab is used outside Tigase or by contributors who want GitHub login, for instance.
Degenerate Deployment without Keycloak as an option: If we want to dispense with operational cost (however minimal) of using Keycloak (such as with Tigase), we can use the same binary to create a degenerate deployment where Sztab directly talks to Tigase LDAP as shown below:
00degSztab, based on Spring Boot, is already set up with a pluggable SecurityConfig, and depending on profiles or environment variables, we can:
- Use spring.security.oauth2.resourceserver.jwt.* for Keycloak+JWT
- Or, bypass JWT entirely and configure spring.ldap.* and LdapAuthenticationProvider for direct LDAP login
Spring Security supports both approaches natively, and we can even externalize this decision via a @ConditionalOnProperty or profile-based bean wiring strategy.
So one binary ==> multiple deployments.
-
Or rather - we do want to base and build the tools around Tigase and with the added support of being LDAP server wouldn't it be easier to use Spring LDAP for authentication directly without any 3rd party?
Wojciech, Spring security does talk Spring LDAP to the external LDAP server: if we use Spring Security, using its configs, it does the required thing: use LDAP to talk to a configured server or use SAML2 to talk to an IdP or use Authorization Code Flow with PKCE for mobile clients or use OAuth/OIDC to talk to OAuth2 servers.
-
Hi all,
I’m proceeding with the current implementation of OAuth2/OIDC authentication via Keycloak, which brings us the flexibility and power of: • Modern authentication flows (Authorization Code, PKCE for mobile, etc.) • Easy integration with external IdPs (Google, GitHub, SAML2, etc.) • Centralized user/role management (with SSO support)
At the same time, I’ve ensured that the same application binary can run in a degenerate mode, where Keycloak is omitted entirely, and authentication is performed directly against a Tigase LDAP server. This makes Sztab easy to adopt in internal deployments with minimal operational overhead.
No code duplication or special builds are required — just a different Spring profile or config file.
Please let me know if there are any concerns before I finalize this track.
— Rk
-
My intention was to point that we want to reduce dependencies (especially on external "applications"). In Tygrys we relied on (tried) Mailu, then on Apache James and 1dev. Now Mailu was replaced by James and 1dev is going to be replaced possibly by Sztab. If we introduce additional "dependencies on external applications" we, sooner or later, may end up needing to replace them. Also we need to maintain them (deploy them end upgrade) in Tygrys on possibly many clusters.
In case of Keycloack, except from "another dependency to deploy and maintain", I wonder about required memory or CPU usage. Do you have any numbers on that?
Why I mention that? We have "wedding planner" cluster, that is almost empty (no data). It has James, MySQL, 1dev, and Tigase XMPP Server and it uses already 52% of the memory (that is over 11GB) and has over 77% of CPU power allocated (that is on 3 nodes cluster with total of 6 cores and 23GB of RAM). We had plans to offer deployment of the same apps on a single node ("personal" tier) that would have just 2 cores (a lot of cores are allocated to clustered Longhorn so reducing no. of nodes would reduce allocation of CPU cores) and 16GB of RAM. And we still need to have some "spare" resources on the host OS and so on.
From your comments I know that Keycloack would be optional and that resolves my issues about CPU and memory usage in Tygrys, but still adding optional dependency (even for our own cluster) would force us to add option to Tygrys Admin UI (or whatever we will call this management app in the future).
-
PR submitted: https://tigase.dev/sztab/~pulls/2
-
Spring Security AuthN Support — Implemented and Merged
This issue tracks the integration of JWT-based authentication using Spring Security 6 / Spring Boot 3.5.
Scope covered:
- Stateless JWT authentication via Spring’s
oauth2ResourceServer().jwt()support - Integration with Keycloak for token issuance and validation
- Role-based authorization using
.hasRole("...")and@PreAuthorize - Default admin user bootstrapped from
application.yml - Security unit test coverage:
- Public vs secure endpoint access
- Role-based access control
- Custom JWT claim mapping via
CustomJwtAuthenticationConverter
Pull Request: https://tigase.dev/sztab/~pulls/2
Key files:
SecurityConfig.java– Defines the primary security filter chainCustomJwtAuthenticationConverter.java– Mapsrealm_access.rolesto Spring authoritiesSecurityConfigTest.java– Focused security test with mock JWTsTestSecurityBeans.javaandTestSecurityOverride.java– Support test slices
The feature branch
feature/sz-4-spring-security-authn-authzhas been merged towolnosc, and all unit tests are passing.Closing this issue as complete. Follow-up enhancements (e.g., fine-grained claims mapping, logout, or multi-tenancy support) can be tracked separately.
—Rk
- Stateless JWT authentication via Spring’s
-
Work Log — Spring Security Authentication (SZ-4)
This section captures the actual time spent implementing, testing, and documenting JWT-based authentication for the Sztab backend.
Task Description Time Spent Analyzing Spring Security 6 behavior and OAuth2 Resource Server support 1.5h Designing SecurityConfigand access control rules (permitAll, hasRole)1.0h Implementing CustomJwtAuthenticationConverter0.5h Writing unit test SecurityConfigTestusing@WebMvcTest, mock JWTs2.0h Supporting test config beans ( TestSecurityBeans,TestSecurityOverride)1.0h Verifying Spring profiles, application.yml merging and test slice behavior 0.5h Final cleanup, file headers, inline documentation, and PR description 1.0h Total 7.5h Note: This includes time spent understanding Spring Security test slices, test config override patterns, and minimizing noise for non-security test cases.
-
Andrzej,
I appreciate the concern around keeping the dependency surface minimal, especially in light of past experiences with Mailu, James, and 1dev.
As you suggest, every external component we adopt must be weighed carefully against operational costs (deployment, upgrades, resource usage). I want to clarify a few points and share some observations that might help put the Keycloak usage in context.
Keycloak Is Optional and Non-Blocking for Tygrys
As you noted, Sztab can function without Keycloak — it’s fully usable with internal testing or basic token injection, and the dependency on Keycloak only comes into play if:
- We want to support real-world OAuth2/OIDC login flows (e.g., SSO, GitHub, Google) (for external customers, not for internal Tigase usage)
- We need fine-grained RBAC/identity federation for external consumers or tiers
For Tygrys itself, this is strictly optional — we don’t plan to force its adoption or bundle it into the default Tygrys deployment.
⸻
Memory and CPU Footprint of Keycloak (Quarkus-based)
The newer versions of Keycloak (since 17.x) run on Quarkus, which significantly reduces their memory and CPU usage compared to the older WildFly versions. Based on tests in our dev clusters:
- Idle memory usage: ~280–350 MB RSS
- CPU usage: Virtually 0 when idle; minimal spikes on login or token validation
- Startup time: ~2–3 seconds cold start with container warmup
This footprint is often smaller than a Spring Boot app or even a MySQL sidecar.
For comparison: • Apache James can easily consume >500 MB RAM depending on configuration • MySQL (with default tuning) often uses ~500–600 MB baseline • Keycloak with embedded DB + small realm + 2 clients = well under 400 MB RAM
If needed, we can set explicit limits in the Helm chart (e.g., 512Mi, 0.25 CPU) and Keycloak will run just fine.
⸻
Impact on Tygrys Admin UI
You’re right that if we choose to expose Sztab’s Keycloak integration via the Tygrys Admin UI (e.g., to configure client secrets or realms), we’d need to account for that in the UI layer.
But we don’t have to surface this in Tygrys unless there’s a clear need. For example: • Sztab-on-prem tiers can ship with a preconfigured Keycloak realm or use external identity providers via federation • Admin UI can defer to Sztab to expose a status endpoint that says “Auth provider: internal / external / Keycloak / none”
This way, Tygrys doesn’t “own” Keycloak — it merely works with Sztab that optionally supports it.
⸻
TL;DR • Keycloak is optional, and never forced into Tygrys • Newer versions are lightweight and resource-efficient • We’re keeping the Sztab security stack modular, so that simpler deployments (like the personal tier) can bypass Keycloak entirely • Happy to revisit this if we run into real-world resource pressure
Let me know if you’d like a test deployment in the wedding planner cluster with memory/CPU limits so we can measure impact more concretely.
-
Just my 2 cents.
Minimal dependency or even no hard-dependency philosophy was something I adopted from the very first day working on Tigase XMPP Server. Even XML parser was implemented in-house. The goal was to have fully functional system without any dependencies. We tried to keep all dependencies optional. And this was result of my past bad experience with dependent libraries, versions, compatibility or even relationship with developers who refused to cooperate to fix or improve stuff.
Another reason was that I wanted to have full control over the code, over licensing, changes we make and directions we want to go.
I know this is not always realistic or financially feasible to implement all in-house. We do not have such resources, even if our knowledge and skills make it possible.
So, keep dependencies to minimum, optional if possible and pay attention to licenses. LGPL, BSD, Apache are all OK. Basically, any open source license which allows us to package it and sell with our own software is OK.
-
I launched a test instance of Keycloak (Quarkus image) with minimal settings and email disabled and I get a POD at idle with near 0 CPU usage and 497MB. This can be reduced with resource constraints:
## My Test Keycloak values.yml: keycloak-test-pod.yml keycloak: username: admin password: admin123 persistence: deployPostgres: false database: host: host.docker.internal port: 5433 user: keycloak password: keycloak123 database: keycloak postgresql: enabled: false% helm install my-keycloak codecentric/keycloak \ --namespace keycloak-test \ --create-namespace \ -f keycloak-test-pod.yml % kubectl get pods --namespace keycloak-test NAME READY STATUS RESTARTS AGE my-keycloak-0 1/1 Running 0 9m46s % kubectl top pod -n keycloak-test NAME CPU(cores) MEMORY(bytes) my-keycloak-0 12m 485Mi % -
Alternatives to Keycloak We can use a feather-light OIDC provider Dex (https://github.com/dexidp/dex) which is written Go. This is a minimal, stateless OIDC gateway with pluggable connectors (LDAP, GitHub, etc.)
Or Ory Hydra - also implemented in Go - an embedded high-performance OAuth2/OIDC provider. But this is headless - not for us I think.
Important: Again, do note that we will not be running this for Tigase internally. This is only an advertised feature of the system for open source users. Open source adopters look for enterprise functions.
-
Important: Again, do note that we will not be running this for Tigase internally. This is only an advertised feature of the system for open source users. Open source adopters look for enterprise functions.
Should we be concerned with this (implement it) for the MVC? Considering that Sztab will be mostly used/focused as a issue management system within Tygrys, where everything will (should) use LDAP so we would have central point of authentication management?
-
If the question is "Is OIDC worth it for MVP?", consider this:
- The cost of implementing JWT-based authentication has been modest — roughly 7 hours of well-contained effort (security configuration, test override beans, and sample JWT parsing via Keycloak claims).
- I deliberately implemented the broader, long-term solution, and then scoped it to match MVP constraints (e.g., OIDC is optional and does not block LDAP or internal auth strategies).
- Implementing narrowly for only the immediate use case (LDAP-only) would, ironically, increase long-term cost — it would make introducing OAuth2 later harder, risk more code churn, and require more retesting.
- Also, this aligns with Artur’s vision of Sztab as a general-purpose OSS offering, not just an internal Tygrys tool. Open source adopters expect enterprise capabilities such as OIDC-capable login.
To summarize: We’re not adding overhead for MVP — we’re preventing future rewrite costs by shaping the function more broadly from the start, without bloating the current delivery. If the time investment still feels disproportionate to MVP scope, I’m happy to absorb the cost of this work outside the project budget. This is an investment in future-proofing the platform.
Here is the remaining effort for OAuth2/OIDC (with Keycloak integration only and with demo of GitHub authentication through OIDC):
OIDC Login Options for Sztab
OIDC Login via Keycloak Only
Estimated Time: ~3.5 to 4 hours
Task Time Notes Configure Spring Security for OAuth2 Login 0.5h Use oauth2Login()in yourSecurityConfigAdd Keycloak client config to application.yml0.5h Includes issuer-uri,client-id,redirect-uriTest login flow (redirect, callback, session) 1.5h Manual browser test + controller access Optional: Map roles via Keycloak claims 0.5h Map realm roles to Spring authorities Document usage and sample Keycloak setup 0.5–1h Add guidance in README or .md file
OIDC Login via GitHub (External IdP)
Estimated Time: ~6 to 7 hours
Task Time Notes Register GitHub OAuth app 0.5–1h Get client ID, secret, set redirect URI Configure Spring Boot with GitHub client details 0.5h Define spring.security.oauth2.client.registration.githubUpdate SecurityConfigwithoauth2Login()0.5h Similar setup as Keycloak Manual testing of login flow 2h Test login, scope consent, callback Handle GitHub token and user attributes 1–1.5h GitHub returns limited claims (e.g., no roles) Optional: Post-login authority mapping 0.5h May be skipped for MVP Documentation (for future devs) 1h Show GitHub App setup + callback URI
Summary
Approach Time Pros Cons Keycloak ~4 hrs Self-contained, rich claims (roles/groups), extensible Adds local Keycloak dependency GitHub ~6–7 hrs Easy for developers, no extra server No role mapping, limited claims, GitHub app setup time
| Type |
New Feature
|
| Priority |
Normal
|
| Assignee | |
| Version |
1
|
| Sprints |
n/a
|
| Customer |
n/a
|
This sub-issue focuses on the authentication (AuthN) setup for the Sztab backend using Spring Security and Keycloak.
Scope includes:
oauth2Login()for Web UI (/ui/**) via Keycloak/api/**) using Keycloak-issued tokensSecurityFilterChainJwtAuthenticationFilterPrincipal, roles, etc.)Note: