mirror of
https://github.com/goauthentik/authentik
synced 2026-05-08 16:13:02 +02:00
* web/flow: extract lifecycle events peripheral to stage management into their own controllers ## What Three features embedded in FlowExecutor, Iframe message handling (from captchas), Multitab message handling, and Websocket message handling, have been extracted from the FlowExecutor and placed into their own controllers. The `renderFrameBackground()` method has been removed. # Why The three features mentioned are all *peripheral* to the task of coordinating challenges. The Iframe message handling may result in a challenge being returned, but there’s a bit of set-up and tear down that doesn’t really correspond well to the central concern of the FlowExecutor; it’s more like a sub-stage of IdentificationStage. By being attached to the executor as Controllers they participate in the executor’s lifecycle and have access to it, but their own internal logic is separated out, making them easier to understand and maintain. As a result, all of the associated machinery– attaching to `window`, disconnecting the websocket client, and so on– can be removed from the FlowExecutor. The `renderFrameBackground()` method is not used. * Darn spelling errors. * Removed debugging line; added some comments. * Restore frame-based backgrounds to executor; fix comments in FlowIframeMessageController * Fix comment. * Prettier and its opinions. * Web/elements/drawer (#21149) * . * . * . * . * . * . * Prettier had opinions. * ## What Componentize the drawer. Remove unused CSS. Provide a better mechanism for manipulating classes than “classMap”; ## Why ### The drawer The drawer was the last thing that we loaded “native” into the UI. This is “the stupidest thing that could work,” just pasting @beryju’s drawer pattern into a component and giving it some functionality. It’s an excellent start to P5 the thing, however. The two portions of the drawer, the “content” and the “panel”, are slots; the content is from the anonymous slot. This mirrors my philosophy that components are for layout and control, but the look and feel of their content should be driven by the content, not the component. ### Remove unused CSS I literally could not find a reason any of these were in the top-level CSS; they don’t set CSS Custom Properties not accessible within the components that use them, they don’t affect the visuals of the components that are present within the top-level DOM, and they were just filling up space. ### class-list ClassMap always bothered me as an especially clunky solution to what is essentially a problem in set theory: the `element.classlist` needs to be adjusted to match “the set of all classes currently active on this component.” ClassList is my solution: a directive that takes a *list* of classes and does the same set-theoretic comparisons as ClassMap, but with a cleaner API. Anything in the list that is a non-empty string is valid: like ClassMap, it will be left or added to ClassList; everything else (`false`, `""`, `null`, `undefined`) will be removed. (Symbols, numbers, and objects are technically possible and will be reject as “not part of the classList set”, but Typescript won’t allow you to pass those in.) This allows us to say things like: const open = (this.open && "pf-m-expanded") || "pf-m-collapsed" ... class="pf-c-drawer ${classList(open)}" … which I think is cleaner than: const open = { "pf-m-expanded": this.open, "pf-m-collapsed": !this.open }; ... class="pf-c-drawer ${classMap(open)}" - \[🦤\] The code has been formatted (`make web`) * Revised comments; changed a variable name. * Update after merge. --------- Co-authored-by: Jens Langhammer <jens@goauthentik.io>
76 lines
2.4 KiB
TypeScript
76 lines
2.4 KiB
TypeScript
import { globalAK } from "#common/global";
|
|
|
|
import type { Interface } from "#elements/Interface";
|
|
|
|
import { multiTabOrchestrateLeave } from "#flow/tabs/orchestrator";
|
|
|
|
import { ChallengeTypes, IdentificationChallenge } from "@goauthentik/api";
|
|
|
|
import { ReactiveController, ReactiveControllerHost } from "lit";
|
|
|
|
type MultitabControllerHost = ReactiveControllerHost &
|
|
Interface & { challenge: ChallengeTypes | null };
|
|
|
|
const isChallenge = (v: unknown): v is ChallengeTypes =>
|
|
v !== null && typeof v === "object" && "component" in v;
|
|
|
|
const isIdentificationChallenge = (v: unknown): v is IdentificationChallenge =>
|
|
isChallenge(v) && v.component === "ak-stage-identification";
|
|
|
|
/**
|
|
* Coordinate with authentication events across multiple tabs
|
|
*
|
|
* @remarks
|
|
*
|
|
* The RedirectStage triggers a change in authentication state. If there is more than one tab open
|
|
* to authentik, this controller receives that event and, after checking with the context to ensure
|
|
* coherency, redirects all tabs to the URL that reflects that state.
|
|
*
|
|
*/
|
|
export class FlowMultitabController implements ReactiveController {
|
|
#abortController: AbortController | null = null;
|
|
|
|
constructor(private host: MultitabControllerHost) {
|
|
/* no op */
|
|
}
|
|
|
|
onMultitab = () => {
|
|
const { challenge } = this.host;
|
|
if (!challenge) {
|
|
return;
|
|
}
|
|
|
|
if (
|
|
isIdentificationChallenge(challenge) &&
|
|
challenge.applicationPreLaunch &&
|
|
challenge.applicationPreLaunch !== "blank://blank"
|
|
) {
|
|
multiTabOrchestrateLeave();
|
|
window.location.assign(challenge.applicationPreLaunch);
|
|
return;
|
|
}
|
|
|
|
const qs = new URLSearchParams(window.location.search);
|
|
const next = qs.get("next");
|
|
if (next) {
|
|
const url = new URL(next, window.location.origin);
|
|
if (!url.pathname.startsWith(`${globalAK().api.relBase}if/flow`)) {
|
|
multiTabOrchestrateLeave();
|
|
}
|
|
window.location.assign(url);
|
|
}
|
|
};
|
|
|
|
hostConnected() {
|
|
this.#abortController?.abort();
|
|
this.#abortController = new AbortController();
|
|
const { signal } = this.#abortController;
|
|
window.addEventListener("ak-multitab-continue", this.onMultitab, { signal });
|
|
}
|
|
|
|
hostDisconnected() {
|
|
this.#abortController?.abort();
|
|
this.#abortController = null;
|
|
}
|
|
}
|