Migrate three render callbacks in oidc-provider (logoutSource,
postLogoutSuccessSource, renderError) from the baked guildPageShell
helper to Nuxt pages under app/pages/auth/, so they go through the
font module and design system instead of a shadow copy.
- Delete guildPageShell (~103 lines of shadow design system).
- Add /auth/logout-success, /auth/oidc-error, /auth/logout-confirm
pages built on dashed-box + btn + main.css tokens.
- renderError now allow-lists error + error_description into query
params and lets Vue default interpolation escape them, closing an
XSS where OIDC error fields were concatenated into raw HTML.
- logoutSource extracts the xsrf from oidc-provider's stable form
output, sets it as an httpOnly 2-minute cookie, and redirects to
/auth/logout-confirm. The confirm page reads the cookie during SSR,
persists the value to useState, and clears the cookie so it's
strictly one-time use. Defensive fallback keeps the raw auto-submit
form if oidc-provider ever changes its form format.
- Fix form actions emitting http:// in production at the root cause:
oidc-provider extends Koa but calls super() with no args, so
app.proxy defaults to false and ctx.protocol ignores
X-Forwarded-Proto. Set _provider.proxy = true after construction;
remove the bogus proxy:true config key (silently ignored) and the
form.replace('http://', 'https://') symptom patch. Make the
x-forwarded-proto override in the catchall conditional on
production + missing header (was unconditional + dead code).
- Add site-wide .btn:focus-visible rule in main.css for WCAG 2.4.7.
Verified in browser: Brygada 1918 loads on all three pages, contrast
ratios pass AA in dark + light, XSS payload escapes to text nodes
only, Set-Cookie: Max-Age=0 enforces one-time xsrf use, no
horizontal overflow at 500px, no console errors.
40 lines
1.4 KiB
TypeScript
40 lines
1.4 KiB
TypeScript
/**
|
|
* Catch-all route that delegates all /oidc/* requests to the oidc-provider.
|
|
*
|
|
* This exposes the standard OIDC endpoints:
|
|
* /oidc/auth — authorization
|
|
* /oidc/token — token exchange
|
|
* /oidc/me — userinfo
|
|
* /oidc/session/end — logout
|
|
* /oidc/jwks — JSON Web Key Set
|
|
*/
|
|
import { getOidcProvider } from "../../utils/oidc-provider.js";
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
const provider = await getOidcProvider();
|
|
const { req, res } = event.node;
|
|
|
|
// The provider's routes config includes the /oidc prefix,
|
|
// so pass the full path through without stripping.
|
|
|
|
// In production, Traefik sets X-Forwarded-Proto: https. Keep a defensive
|
|
// assignment only if the header isn't already present, and never in dev
|
|
// (where forcing https would make oidc-provider emit https://localhost URLs
|
|
// that the browser can't reach). The provider has app.proxy = true, so it
|
|
// honors whatever value is in this header.
|
|
if (
|
|
process.env.NODE_ENV === "production" &&
|
|
!req.headers["x-forwarded-proto"]
|
|
) {
|
|
req.headers["x-forwarded-proto"] = "https";
|
|
}
|
|
|
|
// Hand off to oidc-provider's Connect-style callback
|
|
const callback = provider.callback() as Function;
|
|
await new Promise<void>((resolve, reject) => {
|
|
callback(req, res, (err: unknown) => {
|
|
if (err) reject(err);
|
|
else resolve();
|
|
});
|
|
});
|
|
});
|