Skip to content
Flag of Europe
Made in the European Union · Independently built · Released under EUPL 1.2
jSentinel iris — green eye framed by bracket lids; logo of the project jSentinel iris — green eye framed by bracket lids; logo of the project

jSentinel Lightweight Security for Core Java, REST and Vaadin.

Pluggable authentication, authorization, and annotation-driven protection — wired through Java SPI, no Spring or Jakarta required.

One-prompt setup — Claude Code skills

jsentinel.eu

Capabilities

Annotation-Driven, Three Adapters

Same @RequiresRole / @RequiresPermission annotations protect Vaadin views (navigation listener), REST handlers (filter), and plain Java services (SecuredProxy.wrap(…) dynamic-proxy). One scanner, three call sites.

Framework-Neutral Core

jSentinel-core has no Vaadin, Servlet, or REST-framework dependencies. Adapters map a single decision model to navigation or HTTP status.

Java SPI by Design

Authentication, authorization, audit, action, login-attempt, session-policy, password-hashing, logout — every contract pluggable via META-INF/services/. One JSentinelServiceResolver resolves them all.

Hardened First-Run Bootstrap

The first administrator is created via a one-time token. POSIX 0600 file, atomic creation, PBKDF2-HMAC-SHA256, never logged, never echoed.

Granted / 401 / 403 — That's It

AuthorizationDecision collapses to three outcomes. REST adapters map them to status codes; Vaadin maps them to navigation. Error bodies are short and generic.

Four Reference Demos

Standalone Vaadin (demo-vaadin), JDK-only REST server + CLI (demo-rest), two-tier setup (demo-vaadin-rest-client), and a pure Core-Java CLI library (demo-standalone) — one annotation set, four call shapes. Both Vaadin demos ship a role-admin UI, user CRUD, and an audit-log view.

AI-Assisted Integration

Integrate the full stack from a single prompt — ten Claude Code skills for Vaadin, REST and Standalone (plus persistence & hardening layers). The docs ship an llms.txt map, so coding agents read the library too.

Production Hardening

JSentinelAuditService with 27 sealed event types and a ring-buffer sink. LoginAttemptPolicy brute-force protection with 429 + Retry-After and lockout UI. SessionPolicy with idle/absolute lifetime and session-id rotation after login.

Method Security, Two Ways

Enforce @RequiresRole / @RequiresPermission / @RequiresPolicy on plain Java methods — a compile-time annotation processor generates <Type>Secured wrappers, or SecuredProxy.wrap(…) at runtime. Both route through one JSentinelEnforcer.

Policy API

Beyond roles and permissions: a typesafe policy builder, @RequiresPolicy, and a sealed PolicyDecision (Allowed / Denied / StepUpRequired) with resource-aware, owner-or-admin style checks.

Multi-Tenancy + Active-Session Role Refresh

TenantId scopes every store key. JSentinelVersion drift detection refreshes roles for live sessions — revoke a role and the next request is rejected (Vaadin reroute / REST 401), no waiting for expiry.

Store-Agnostic Persistence

11 small store SPIs with in-memory defaults, an Eclipse Store reference backend, and a contract testkit. Swap to durable storage with one dependency — no code change. Redis / JDBC are drop-in.

Credential Hardening + Compliance

A standards-aligned credential stack: Argon2id / bcrypt / scrypt, post-KDF HMAC pepper, breached-password checks (Have I Been Pwned), abuse detection — mapped to OWASP ASVS V2, NIST SP 800-63B and 40 CWE classes.

Multi-Session Logout & Action Audit

LogoutService.logout(SubjectId, LogoutScope) signs out one or every session. ActionAuthorizationService emits an ActionDenied audit event on every denial — no manual instrumentation.

Security Event Bus

New in 00.75 — typed security events in signed (Ed25519) envelopes with replay protection, per-(tenant, producer) ordering, producer policy and a REST/SSE bridge. Audit becomes a consumer; revoke a session and every node can hear it. Restart-safe Eclipse-Store stores.

JWT Validation

New in 00.76 — RFC-conformant JWT validation (jSentinel-jwt): a disciplined JWKS client, a mandatory algorithm allow-list with hard algorithm-confusion defence (alg:none & key-confusion rejected), strict iss / aud — wired in three config lines via .jwt(...). JOSE stays isolated to one module.

OAuth2 Login (RP)

New in 00.77 — OAuth2 Relying-Party flows: Authorization-Code + PKCE, private_key_jwt client auth, refresh-token rotation with reuse-detection, introspection, revocation and the Device Grant — via .oauth2(...), across Vaadin / REST / Standalone.

OpenID Connect Login

New in 00.78–00.79 — OIDC Relying-Party: ID-token validation (nonce / azp / at_hash), discovery, UserInfo, claims→subject mapping and acr step-up via .oidc(...). One-line vendor profiles (Keycloak, Entra, Auth0, Okta, Google, GitHub) — and the JWT / OAuth2 / OIDC API is now stable.

Mutation-Tested Library

Line coverage tells you tests executed the code.
Mutation testing changes the bytecode at runtime and asks the harder question: did the tests catch the change?

Line coverage
Code ran during a test
Easy to game
vs.
Mutation coverage
Tests detected a real bug
What you actually want

A surviving mutation is a real gap. Here's where the library stands.

jSentinel-core
86%
mutation coverage
2076 of 2413 mutations killed · as of 00.75.20
Mutation
86%
jSentinel-rest
95%
mutation coverage
95 of 100 mutations killed · as of 00.75.20
Mutation
95%
jSentinel-standalone
98%
mutation coverage
44 of 45 mutations killed · as of 00.75.20
Mutation
98%
jSentinel-vaadin
80%
mutation coverage
249 of 313 mutations killed · as of 00.75.20
Mutation
80%
jSentinel-processor
78%
mutation coverage
54 of 69 mutations killed · as of 00.75.20
Mutation
78%
jSentinel-persistence-eclipsestore
73%
mutation coverage
265 of 361 mutations killed · as of 00.75.20
Mutation
73%
jSentinel-crypto-bc
62%
mutation coverage
124 of 199 mutations killed · as of 00.75.20
Mutation
62%
jSentinel-credentials-hibp
57%
mutation coverage
43 of 76 mutations killed · as of 00.75.20
Mutation
57%
jSentinel-events · federation stack
85%
mutation coverage
379 of 445 mutations killed · as of 00.75.20
Mutation
85%
jSentinel-jwt · federation stack
75%
mutation coverage
135 of 180 mutations killed · as of 00.79.20
Mutation
75%
jSentinel-oauth2 · federation stack
65%
mutation coverage
210 of 324 mutations killed · as of 00.79.20
Mutation
65%
jSentinel-identity-oidc · federation stack
72%
mutation coverage
170 of 235 mutations killed · as of 00.78.00
Mutation
72%
jSentinel-dpop · federation stack
63%
mutation coverage
57 of 91 mutations killed · as of 00.79.10
Mutation
63%

Mutation coverage = killed mutations / total mutations. Mutation testing rewrites the bytecode and asks whether the tests catch the change — a far harder bar than line coverage. The eight core/adapter modules were last re-measured reactor-wide in 00.75.20 (touched-module PIT pass, no module regressed); the newer federation modules (events, JWT, OAuth2, OIDC, DPoP) show the latest pass cited in their own release notes — some are first-PIT-pass and still climbing. Click a core/adapter tile for its full site-native report (per-mutator breakdown, surviving-mutant list); federation tiles link to the release notes that measured them, pending a rendered per-module report. All reports: Mutation Reports.