JanssenProject / JanssenProject/jans
fix(jans-auth-server): Agama flow pages are served with no framing protection
- Dominant language
- Java
- Stars
- 647
- Forks
- 173
- Avg merge
- 1d 18h
- Merged PRs (30d)
- 110
Description
## Description
Agama flow pages (login, MFA, passkey and any deployed Agama project) are served without
`X-Frame-Options` or `Content-Security-Policy: frame-ancestors`. Any site on the internet can
embed them in an iframe, which makes the affected flows vulnerable to clickjacking / UI redressing
— an attacker overlays their own UI on a real login frame and induces the user to complete an
authentication ceremony they didn't intend.
## Why the existing protection doesn't cover Agama
`jans-auth-server/server/src/main/java/io/jans/as/server/filter/HeadersFilter.java:46`
```java
if (requestURI.contains(".htm") || hasAny) {
httpResponse.addHeader(X_FRAME_OPTIONS_HEADER, appConfiguration.getXframeOptionsHeaderValue().getValue());
}
```
The header is emitted only when the request URI contains `.htm`, or matches
`applyXFrameOptionsHeaderIfUriContainsAny` — which defaults to an **empty list**
(`AppConfiguration.java:2152`).
Agama pages are served by `ExecutionServlet` under `/fl/` with a `.fls` suffix
(`ExecutionServlet.java:20-27`). `.fls` does not contain `.htm`, so no `X-Frame-Options` is
emitted. There is also no `Content-Security-Policy` set anywhere in the auth server (grep for
`Content-Security-Policy` across `jans-auth-server`: zero hits).
Net effect: JSF-based pages get `SAMEORIGIN`, Agama-based pages get nothing.
## Reproduction
1. Deploy any Agama flow (e.g. the agama-passkey project).
2. `curl -I https:///jans-auth/fl/.fls` — observe no `X-Frame-Options` and no
`Content-Security-Policy` in the response.
3. Serve a page on an unrelated origin containing
`` — it renders.
## Mitigation available today
Admins can set a CSP through Agama's existing response-header map
(`BaseServlet.java:142` applies `engineConf.getDefaultResponseHeaders()` to every flow page):
```json
"agamaConfiguration": {
"defaultResponseHeaders": {
"Cache-Control": "max-age=0, no-store",
"Content-Security-Policy": "frame-ancestors 'self'"
}
}
```
This is opt-in and undocumented, so no existing deployment has it.
## Proposed fix
1. Ship `frame-ancestors 'self'` as the **default** for Agama flow pages, so deployments are safe
without configuration.
2. Alternatively/additionally, include `.fls` in the default
`applyXFrameOptionsHeaderIfUriContainsAny` so the existing `X-Frame-Options` path covers Agama.
3. Document `defaultResponseHeaders` as the supported way to relax this for deployments that
intentionally embed flows.
Note that a per-client `frame-ancestors` allowlist (derived from the client's registered origins)
is a separate enhancement — it's what an embedded/iframe authentication flow would need, and is
tracked under GluuFederation/agama-passkey#13. This issue is only about closing the default-open
state.
## Impact
- Affects: all Agama flows, all versions shipping the Agama engine
- Severity: medium — requires user interaction and a convincing overlay, but the target is an
authentication ceremony
- Not exploitable for credential theft directly; the risk is inducing unintended authentication
or consent
Contributor guide
Assessment
This issue has not been assessed yet.