This page covers what you must configure to run ASEE Flow WebAdmin and its engine REST API safely in production. It builds on the Authentication and Deployment pages.
The demo ships deliberately permissive defaults for local development — a public client secret, open CORS (*), no audience validation, and (optionally) the no-auth none mode. Every one of these must be overridden before production. The checklist at the end of this page lists them.
Choose a production authentication mode
| Mode | Production? | Notes |
|---|
basic | ✅ | HTTP Basic; credentials travel in every request, so require HTTPS. Good for headless/API clients. |
form | Internal only | Session-cookie login for the browser. External REST clients can’t authenticate (they get 401). Use when the only REST caller is the admin browser. |
oauth2 | ✅ | Standard OIDC (Okta, Entra ID, custom). Supports Bearer tokens for external clients when the resource server is configured (below). |
keycloak | ✅ | OIDC plus live Keycloak directory integration (users, groups, tenants). Best for multi-tenant setups. |
none | ❌ never | No authentication — installs a synthetic demo user and permits all requests. Development only. |
none is for local development only. As a safeguard, the starter refuses to start in none mode unless a development Spring profile (dev, demo, test, or local) is active — so it cannot be silently left enabled in production. For any real deployment, set authentication to basic, oauth2, or keycloak.
Never leave the engine REST API unprotected
aseeflow.webadmin.disable-rest-security removes /engine-rest/** from WebAdmin’s security chain. Its safety depends on the deployment:
- Spring Boot (in-process engine) — there is no other security layer. Setting
disable-rest-security: true leaves /engine-rest/** fully open: every request returns 200, even with wrong credentials. Keep it false.
- WAR — the separate engine REST WAR enforces its own authentication, so
disable-rest-security: true is the correct default there (WebAdmin must not try to secure endpoints it doesn’t contain). See Deployment.
If you ever disable REST security in a Spring Boot deployment, you must protect /engine-rest with a separate layer (reverse-proxy auth, mTLS, or network policy).
Also enable the engine’s authorization framework in production so the engine enforces per-user permissions:
camunda.bpm:
authorization:
enabled: true
Secure the engine REST API (OIDC modes)
For oauth2 and keycloak, Bearer-token acceptance on /engine-rest is off by default and activates only when you set an issuer:
spring.security.oauth2.resourceserver.jwt:
issuer-uri: ${keycloak.url.auth}/realms/aseeflow
audiences:
- account
Set audiences in production. With an empty audience list, only the issuer is validated — in a multi-tenant Keycloak realm, any token the realm signs is accepted, which is a privilege-escalation hole. List the audience your tokens actually carry.
Keycloak access tokens carry aud: "account" by default, not your client ID. To validate against the client ID instead, add an Audience mapper on the client in Keycloak (Clients → client → Client scopes → dedicated → Add mapper → Audience), then list that client ID in audiences.
The REST chain always accepts the SSO session cookie; configuring the resource server makes it accept both a session and a Bearer token. There is no token-only/session-free REST surface.
Map identities correctly
Each login bridges the authenticated principal into the engine as a user ID plus resolved groups and tenants. Two misconfigurations cause silent lockouts:
- User ID mismatch. The
user-name-attribute claim (e.g. preferred_username, sub, email) becomes the engine user ID. If authorizations were assigned to a username but the token maps a UUID, the user logs in but resolves to zero groups and zero tenants — every query comes back empty. Make sure the claim matches the IDs the engine knows.
- Missing group claim. In
oauth2 mode the engine reads groups from a token claim (camunda.bpm.oauth2.identity-provider.group-name-attribute, default groups). If your provider doesn’t emit that claim, add a groups mapper on the provider. In keycloak mode the identity provider plugin queries Keycloak directly, so token group claims aren’t used.
Manage secrets
- Never hard-code the OIDC client secret. Inject it via an environment variable (
KEYCLOAK_CLIENT_SECRET) and keep the YAML referencing ${KEYCLOAK_CLIENT_SECRET:...}.
- The demo’s fallback client secret and the
demo / demo user are public and demo-only. Rotate the secret in your identity provider and create real users before production.
- Never commit secrets, database passwords, or TLS keys. If one leaks into git history, rotate it immediately.
Transport security
-
Serve WebAdmin over HTTPS. Basic and form modes send credentials or session cookies that must not travel in clear text.
-
disableSSLCertificateValidation is development-only. The demo sets it true for a local self-signed Keycloak; in production remove it (or set false) and use a CA-trusted certificate on your issuer.
-
Behind a reverse proxy / load balancer: terminate HTTPS at the proxy and forward auth headers transparently. Register Keycloak redirect URIs against the external HTTPS URL (e.g.
https://webadmin.example.com/webadmin/login/oauth2/code/keycloak), not the internal one, or OIDC login redirects break.
-
For session-based modes, set the session cookie to
SameSite=Lax as a baseline CSRF defense:
server.servlet.session.cookie.same-site: lax
Restrict CORS
CORS only matters when a cross-origin browser client (not the bundled UI) calls the REST API — relevant in oauth2 mode. The default is open (["*"]); restrict it to your real origins:
aseeflow:
webadmin:
cors:
allowed-origins:
- https://admin.example.com
Production hardening checklist
Verify your configuration
Confirm endpoints are actually protected. Unauthenticated requests must be rejected:
curl -s -o /dev/null -w "HTTP %{http_code}\n" \
"http://localhost:8080/engine-rest/process-definition/count"
# Expected: 401 (or a 302 login redirect in form mode) — never 200
Valid credentials succeed, wrong credentials fail (basic mode shown):
curl -s -o /dev/null -w "%{http_code}\n" -u demo:demo "http://localhost:8080/engine-rest/process-definition/count" # 200
curl -s -o /dev/null -w "%{http_code}\n" -u demo:wrong "http://localhost:8080/engine-rest/process-definition/count" # 401
For OIDC modes, fetch a token and call REST with it:
TOKEN=$(curl -s -X POST \
"http://localhost:9000/auth/realms/aseeflow/protocol/openid-connect/token" \
-d grant_type=password -d client_id=aseeflow-identity-service \
-d "client_secret=$KEYCLOAK_CLIENT_SECRET" \
-d username=demo -d password=demo | jq -r .access_token)
curl -s -o /dev/null -w "HTTP %{http_code}\n" \
-H "Authorization: Bearer $TOKEN" \
"http://localhost:8080/engine-rest/process-definition/count" # 200
Check that identities resolve — an authenticated user should see their groups:
curl -s -u demo:demo "http://localhost:8080/engine-rest/group?maxResults=50"
# Empty result usually means an identity-mapping problem (see "Map identities correctly").