{
"host": "app.demo.localhost",
"path": "/*",
"frontend": true,
"backend": "http://127.0.0.1:8080",
"auth": {},
"inject": "bundle.json",
"userBadge": {
"position": "right",
"claims": ["preferred_username", "email"],
"roles": ["admin"],
"logoutLabel": "Sign out"
},
"uiGate": [
{ "selector": ".danger-zone", "roles": ["admin"] },
{ "selector": ".beta-feature", "hidden": "before" },
{ "selector": "[data-feature=export]", "auth": "before" }
],
"authGuard": { "forbidden": { "banner": true } },
"csp": { "mode": "nonce" }
}Injectable UX
Useful if you want to…
show who is logged in and a logout button on an application you cannot modify;
turn a plain 401 into a login tab that replays the request after login, without a page reload;
hide or gate controls by role, scope or claim client-side, even when the markup is fixed;
add a banner, a stylesheet, a script or a PWA manifest per route;
show a readable page or a banner when the gateway itself refuses a request on identity (403), while keeping the JSON 403 for
fetch().
The application source does not change. Gate One injects a small guard script and, optionally, an operator-provided bundle.
What can be injected
| Feature | What it does |
|---|---|
User badge | Collapsed username on the top edge; expands to show chosen
claims/roles/scopes and a logout button. Configured per route with
|
Guard | Intercepts the gateway’s 401, opens the login surface in a tab,
replays the failed request after login and evaluates UI gating. Active
on frontend routes; opt out with |
UI gating |
|
Inject bundle | HTML/JS/CSS insertions at a document position ( |
PWA | Manifest, icons, service worker and API cache policy per route.
Identity endpoints must be excluded from the cache ( |
Forbidden UX |
|
CSP |
|
Example configuration
The bundle referenced by inject:
{
"injects": [
{
"name": "announcement",
"target": "body-end",
"when": "/dashboard",
"file": "banner.html"
},
{
"name": "brand-style",
"target": "head",
"style": ".demo-brand{letter-spacing:.02em}"
}
],
"patch": [
{
"selector": "[data-feature=beta]",
"attrs": { "data-gtw-auth-hidden": "before" }
}
]
}Complete files:
routes.json,
bundle.json.
Placing content at a specific element
injects[].target is a document position, not a selector. It accepts
only head (default) or body-end, plus order for the position within
that target and when for a path prefix. There is no
"insert next to `.article`" field.
Two mechanisms cover placement, and they are different:
| Mechanism | What it can do |
|---|---|
| Sets attributes on the elements a CSS |
| The injected JavaScript runs in the page context, so
it can query the DOM and place content wherever it belongs
( |
To anchor generated markup to an existing element, inject a small script
at body-end and let it do the placement:
{
"name": "article-note",
"target": "body-end",
"when": "/wiki/",
"script": "var a=document.querySelector('#mw-content-text'); if (a) a.insertAdjacentHTML('afterbegin', '<p class=article-note>Reviewed by the platform.</p>');"
}The same pattern with a larger script is the
proxied-site example: the bundle injects
the Wikipedia+ userscript at head, and the script builds its panel
and positions the arrow relative to that panel. target only decides
where the <script> tag lands in the document.
Scripts execute once when the injected markup is parsed. If the page
rerenders its DOM, the script is not replayed; patch is the mechanism
that the guard re-applies (see the limits below).
PWA and identity endpoints
pwa.enabled adds a service worker that caches pages and assets. Any
URL that carries identity must be excluded with
pwa.networkOnly.excludePatterns (JS regexes tested against the full
URL): the identity provider’s discovery, authorize, token, JWKS and
end-session endpoints; any OAuth/JWT token endpoint used by API or
machine clients; the http-bridge login URL; and the Gate One callback
(/auth/callback) or the embedded IAM’s /t/<tenant>/callback.
A cached auth probe replays a stale identity after login or logout; a
cached callback replays a one-time code. The
Injectable UX example shows the
patterns, and
routes[.pwa.networkOnly]
is the field reference.
Markup or config?
If you control the HTML, prefer the data-gtw- attributes; they are
explicit and survive selector changes. If you cannot modify the
application, use uiGate rules by selector. Markup wins: an element
carrying a data-gtw- attribute ignores the equivalent uiGate rule.
Activation is gated; deactivation is always free. Unchecking a box,
closing a details, resetting a form or returning a value control to
its OFF value never opens a login. This avoids trapping anonymous users
in an action they cannot complete.
Own account UI
If the application owns its navigation, omit userBadge and render the
session in the application’s own burger menu or side panel. Gate One
still injects the guard and exposes the session probe; the account
markup stays in the application. See
Bring your own account panel and the
account panel example.
External sites
A route with upstream can proxy an external site and inject a
userscript into every HTML page. The browser installs no extension and
the upstream site contains no Gate One code. See
Enhance a proxied site.
CSP is the real constraint
The guard uses classes and a nonced runtime <style>; it does not need
style="…" attributes. If the application already sends a CSP:
csp.mode: "merge"keeps the origin policy and appends the guard hashes;csp.mode: "nonce"emits Gate One’s policy and stamps inline scripts;csp.mode: "hash"avoids rewriting the body but requires external application scripts;own: trueremoves the origin CSP only innonce/hashmode and is opt-in and logged.
Injected content must satisfy the policy. A nonce never applies to a
style attribute.
The full CSP guide — the four modes, styles, rollout and violation
reading — is Get a strict Content Security
Policy. The data-gtw-* attribute contract behind uiGate is
Declarative UI gating.
Important |
|
Configuration reference
See also
Guides — task-oriented deep dives: strict Content Security Policy and declarative UI gating.