Merge pull request 'portal: Zielmodul portal beim Token-Abruf (B-05)' (#397) from feat/portal-token-modul into main
CI / Web-Barrierefreiheitspruefung (axe, Tastatur, Reflow, Textabstaende) (push) Canceled after 0s
CI / Core-Modul (Build/Vet/Test) (push) Successful in 59s
CI / DMS Security-Test-Gate (AC5, Merge-Gate) (push) Successful in 4s
CI / Connect Security-Test-Gate (QA-01 AC4, Merge-Gate) (push) Successful in 6s
CI / DMS-Modul Supply-Chain-Scan (govulncheck) (push) Failing after 18s
CI / Web (dms-browser) (push) Failing after 1m23s
CI / Web (pdf-viewer) (push) Failing after 57s
CI / Web (shl) (push) Failing after 13s
CI / DMS-Modul (Lint/Build/Test/Coverage/Artefakt) (push) Failing after 2s

This commit was merged in pull request #397.
This commit is contained in:
patrick committed 2026-10-28 11:32:50 +01:00
commit ff79bf7514
4 files changed
+29 -1

No files matched your search

+9
View File
@@ -11,6 +11,7 @@ Browser-seitig (werden beim Build eingebettet):
|---|---|---|
| `NEXT_PUBLIC_CORE_API_BASE` | ja | Basis-URL des Core (`/auth/module-token`) |
| `NEXT_PUBLIC_LOGIN_URL` | nein | Ziel der Weiterleitung, wenn die Sitzung fehlt |
| `NEXT_PUBLIC_TOKEN_MODULE` | nein | Zielmodul beim Token-Abruf (`?module=portal`); nur setzen, wenn der Core `portal` in `NEXARCH_ACCOUNT_MODULE_AUDIENCES` führt |
Server-seitig (nur Route `/api/visible-modules`, nie im Browser sichtbar):
@@ -30,6 +31,14 @@ Server-seitig (nur Route `/api/visible-modules`, nie im Browser sichtbar):
Fehlt `PORTAL_POLICY_API_URL`, `PORTAL_POLICY_SERVICE_TOKEN` oder die Core-URL, antwortet die Route mit 503.
## Zielmodul `portal` einführen
Reihenfolge beim Ausrollen: (1) Core: `portal` in `NEXARCH_ACCOUNT_MODULE_AUDIENCES` aufnehmen; (2) Portal: `NEXT_PUBLIC_TOKEN_MODULE=portal` (Build) und `PORTAL_TOKEN_AUDIENCE=portal` (Server) setzen. Umgekehrt scheitert der Token-Abruf, weil der Core ein unbekanntes Zielmodul ablehnt. Danach akzeptiert das Portal keine für `dms` ausgestellten Tokens mehr.
## Netz
Die Statusseite (`PORTAL_STATUS_API_URL`) ist selbst nicht authentifiziert und darf nur vom Portal-Server aus erreichbar sein (Firewall); die Kennzahlen- und Workflow-APIs ebenfalls nur intern.
## Verhalten der Route
- `GET /api/visible-modules` verlangt `Authorization: Bearer <Modul-Token>`; Signatur (Ed25519, JWKS vom Core) und Ablaufzeit werden geprüft, sonst 401.
+1
View File
@@ -10,6 +10,7 @@ export function getSession(): Session {
}
session = createSession({
coreBase,
module: process.env.NEXT_PUBLIC_TOKEN_MODULE || undefined,
onLoginRequired: () => redirectToLogin(process.env.NEXT_PUBLIC_LOGIN_URL),
});
}
+17
View File
@@ -156,3 +156,20 @@ describe("UX-10: Weiterleitung zur Anmeldung", () => {
expect(assign).not.toHaveBeenCalled();
});
});
describe("Zielmodul im Token-Abruf", () => {
it("haengt ?module=<name> an, wenn gesetzt, und laesst es sonst weg", async () => {
const urls: string[] = [];
const mk = (module?: string) => {
const fetchImpl = vi.fn(async (input: RequestInfo | URL) => {
urls.push(String(input));
return tokenReply(claims);
}) as unknown as typeof fetch;
return createSession({ coreBase: "/api/core/v1", module, fetchImpl });
};
await mk("portal").token();
await mk().token();
expect(urls[0]).toBe("/api/core/v1/auth/module-token?module=portal");
expect(urls[1]).toBe("/api/core/v1/auth/module-token");
});
});
+2 -1
View File
@@ -39,6 +39,7 @@ export type IdentityLoader = () => Promise<Identity | null>;
export interface SessionOptions {
coreBase: string;
module?: string;
fetchImpl?: typeof fetch;
now?: () => number;
onLoginRequired?: () => void;
@@ -99,7 +100,7 @@ export function createSession(opts: SessionOptions): Session {
async function refresh(): Promise<string> {
let res: Response;
try {
res = await fetchImpl(`${opts.coreBase}/auth/module-token`, {
res = await fetchImpl(`${opts.coreBase}/auth/module-token${opts.module ? `?module=${encodeURIComponent(opts.module)}` : ""}`, {
method: "POST",
credentials: "include",
headers: { Accept: "application/json" },