Watch
1
0
Fork
You've already forked jme-jwe-example
0
mirror of https://github.com/jme-admin-ch/jme-jwe-example.git synced 2026-08-17 13:04:59 +00:00
Read-only mirror of https://github.com/jme-admin-ch/jme-jwe-example — Bundesamt für Informatik und Telekommunikation. Issues & pull requests at the source. Catalog: https://www.opensource.admin.ch/en/softwares/mdcgcp https://www.opensource.admin.ch/en/softwares/mdcgcp
  • Java 59.3%
  • TypeScript 24.4%
  • HTML 9.7%
  • JavaScript 4.8%
  • SCSS 1.5%
  • Other 0.3%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-08-14 11:17:05 +02:00
.github/workflows JEAP-7322 Adapted github build workflow 2026-07-30 07:47:57 +02:00
.mvn JEAP-7096: add repository skeleton with OSS files and root pom 2026-07-03 15:07:17 +02:00
.run JEAP-7096: demonstrate two Vault transit key versions with rotation grace 2026-07-03 15:07:18 +02:00
docker Update All dependencies 2026-08-06 14:17:45 +00:00
jme-jwe-auth-scs Update All dependencies 2026-08-13 23:03:44 +00:00
jme-jwe-scs chore: Clean up package-lock.json by removing unused dependencies 2026-08-14 11:07:44 +02:00
jme-jwe-test Update All dependencies 2026-08-13 23:03:44 +00:00
.gitignore JEAP-7096: add repository skeleton with OSS files and root pom 2026-07-03 15:07:17 +02:00
CHANGELOG.md chore: Downgrade angular-eslint dependency to version 21.0.0 2026-08-14 10:39:26 +02:00
CONTRIBUTING.md JEAP-7096: add repository skeleton with OSS files and root pom 2026-07-03 15:07:17 +02:00
exclude-patterns.txt JEAP-7096: add repository skeleton with OSS files and root pom 2026-07-03 15:07:17 +02:00
LICENSE JEAP-7096: add repository skeleton with OSS files and root pom 2026-07-03 15:07:17 +02:00
mvnw JEAP-7096: add repository skeleton with OSS files and root pom 2026-07-03 15:07:17 +02:00
mvnw.cmd JEAP-7096: add repository skeleton with OSS files and root pom 2026-07-03 15:07:17 +02:00
pom.xml Update All dependencies 2026-08-13 23:03:44 +00:00
publiccode.yml Update version/date in publiccode.yml 2026-08-14 11:17:05 +02:00
README.md JEAP-7097 add JWE decrypt demo view backed by POST /api/demo/decrypt 2026-07-08 13:00:23 +02:00
SECURITY.md JEAP-7096: fix security advisory link copied from the process context example 2026-07-03 15:07:17 +02:00
setPomVersions.sh JEAP-7096: add repository skeleton with OSS files and root pom 2026-07-03 15:07:17 +02:00
THIRD-PARTY-FRONTEND-LICENSES.md JEAP-7308 2026-08-04 13:20:55 +02:00
THIRD-PARTY-LICENSES.md Updating THIRD-PARTY-LICENSES.md 2026-08-14 10:16:25 +02:00

jme-jwe-example

Example project demonstrating transparent JWE end-to-end encryption between an Angular frontend and a Spring Boot backend using the jEAP JWE starter and its frontend companion @jeap/jeap-jwe-client.

The example is a self-contained system (SCS) and consists of the following modules:

  • jme-jwe-scs: The self-contained system
    • jme-jwe-scs-web: Spring Boot backend using the jEAP JWE starter together with the jEAP security starter (OAuth2 resource server) and the jEAP web config starter
    • jme-jwe-scs-ui: Angular frontend using Oblique and the jEAP JWE client, packaged into the backend jar as static resources
  • jme-jwe-auth-scs: An instance of the jEAP OAuth mock server used as authorization server
  • jme-jwe-test: Automated integration tests covering the local Docker Compose setup

Showcased cases

Case Endpoint Transport
Encrypted POST POST /api/persons Request body encrypted as compact JWE (application/jose), response encrypted with the request-local response key
Encrypted GET GET /api/persons Response encrypted with the key from the JWE-Response-Key header
Unencrypted request with allowlist GET /api/public/info Plain JSON — /api/public/** is listed in jeap.jwe.filter.excluded-paths, but the endpoint still requires a valid Bearer token
Decrypt demo view POST /api/demo/decrypt Paste a captured request JWE and see its protected header and decrypted payload — the decrypt round trip itself travels encrypted

Encryption and decryption are transparent on both sides: the Spring controllers only see plain JSON, and the Angular services use the ordinary HttpClient. The jEAP JWE servlet filter and the jeapJweInterceptor handle the JWE protocol, driven by the configuration the backend publishes at /.well-known/jwe-configuration (see jeap.jwe.* in application.yml).

The "Decrypt JWE" view is strictly a demo feature: paste a compact JWE captured from the browser's network tab (e.g. the application/jose request body of an encrypted POST, or the JWE-Response-Key header value) and the backend decrypts it with its private keys, so the UI can display the colored JWE segments, the protected header and the recovered plaintext. The endpoint is a deliberate decryption oracle and must never appear in a production application. Response JWEs (alg: dir) are encrypted with the request-local CEK the backend never retains, so they can only be decrypted by additionally pasting the JWE-Response-Key header value of the same request: the backend unwraps that envelope with its private key to recover the CEK — the same handshake the JWE servlet filter performs when encrypting a response.

Key management is backed by the HashiCorp Vault transit secret engine: the Docker Compose setup creates an exportable rsa-4096 transit key and rotates it once (see docker/docker-compose.yml), so two key versions exist. The backend serves both public keys at /.well-known/jwks.json with the newest version first (jme-jwe-scs-key:2), the frontend encrypts against the newest one, and payloads encrypted with the previous version (jme-jwe-scs-key:1) are still decrypted — the rotation grace that keeps clients with a cached JWKS working across a key rotation.

Changes

This library adheres to Semantic Versioning. Changes are documented in CHANGELOG.md.

Prerequisites

  • JDK 25 or later
  • Docker (for Vault)
  • Node.js 22 / npm (only needed to run the frontend independently with ng serve)
  • Google Chrome (used headless by the browser-based integration test in jme-jwe-test)
  • Use the maven wrapper ./mvnw to build the project

Getting started

Infrastructure

Start Vault (dev mode, with the transit engine and JWE key created automatically):

docker compose -f docker/docker-compose.yml up

Vault is published on http://localhost:8201 (root token root) to avoid clashing with other locally running Vault instances.

Build

./mvnw install

This also runs the frontend build and tests via npm (an npm ci is triggered automatically when node_modules is missing).

Start

Start the OAuth mock server first, then the SCS:

./mvnw --projects jme-jwe-auth-scs spring-boot:run -Dspring-boot.run.profiles=local
./mvnw --projects jme-jwe-scs/jme-jwe-scs-web spring-boot:run -Dspring-boot.run.profiles=local

Then open http://localhost:8080/jme-jwe-scs/ and log in as the predefined user. Open the browser's network tab to see the encrypted requests and responses (application/jose) as well as the plain allowlisted request.

Independent UI

To develop the frontend with a live dev server, start the backend as described above and run:

cd jme-jwe-scs/jme-jwe-scs-ui
npm start

The dev server on http://localhost:4200 proxies all backend calls to http://localhost:8080 (see proxy.conf.js).

Integration tests

The jme-jwe-test module covers the Docker Compose setup with automated tests: it uses Spring Boot's Docker Compose support to automatically start and stop the Vault infrastructure, then starts the two Spring Boot services as Maven subprocesses via mvnw spring-boot:run and polls their health endpoints. The tests then exercise the JWE protocol end-to-end with real Vault-backed keys: encrypted POST, encrypted GET, the unencrypted allowlisted request, plaintext rejection, the JWKS endpoint serving both Vault transit key versions (newest first), and the rotation grace — an encrypted request using the previous key version is still accepted.

The module also contains a browser-based end-to-end test (JweExampleBrowserIT) that drives the real Angular UI in headless Chrome with Playwright: it logs in through the OAuth mock server's login page (authorization code flow with PKCE, exactly as a user would) and then asserts on the captured network traffic that /api/persons is transported as application/jose (request body encrypted in the browser, response a compact JWE) while the allowlisted /api/public/info stays plain application/json. It also drives the decrypt demo view: a request JWE captured from the traffic is pasted, decrypted by the backend and displayed, while the decrypt round trip itself stays encrypted on the wire. Playwright uses the locally installed Google Chrome (chrome channel), so no browser download is required.

# Build and install all local modules (required: the services are forked from the local build)
./mvnw install -pl '!:jme-jwe-test'
# Run all integration tests
./mvnw verify -pl jme-jwe-test
# Run only the browser-based test
./mvnw verify -pl jme-jwe-test -Dit.test=JweExampleBrowserIT
# Skip the integration tests in a full build
./mvnw install -Dskip.failsafe.tests=true

Ensure Docker is running and the ports 8080, 8081 and 8201 are available.

The jme-jwe-scs-web module additionally contains an integration test that runs without any infrastructure by using static JWE test keys (jeap.jwe.test.enabled=true with jeap-spring-boot-jwe-test).

Running on CI

On CI, the CI environment variable activates the ci Spring profile, which layers docker-compose-ci.yml over the compose file and reaches the containers through an isolated Docker network instead of published localhost ports.

Note

This repository is part of the open source distribution of jEAP. See github.com/jeap-admin-ch/jeap for more information.

License

This repository is Apache-2.0 licensed, see LICENSE.