web: Flesh out stage mapping error handling.#20292
Conversation
85b727a to
48abf6a
Compare
✅ Deploy Preview for authentik-docs ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
| return this.renderChallengeError( | ||
| `No stage found for component: ${challenge.component}`, | ||
| ); |
| try { | ||
| mapping = await StageMapping.from(stageEntry); | ||
| } catch (error: unknown) { | ||
| return this.renderChallengeError(error); |
| return staticHTML`<${unsafeStatic(tag)} ${props}></${unsafeStatic(tag)}>`; | ||
| } | ||
|
|
||
| protected renderChallengeError(error: unknown): SlottedTemplateResult { |
There was a problem hiding this comment.
This was inspired from #20261, splitting off a rendering state into a method. This also let's us handle arbitrary errors encountered while rendering.
| return challenge?.component | ||
| ? until(this.renderChallenge(challenge)) | ||
| : this.renderLoading(); | ||
| })} |
There was a problem hiding this comment.
My initial testing with some mocked errors showed the console.trace(...) twice. It seems that Lit will trigger a second render when loading changes. Using guard here will prevent a second challenge while juggling the delicate aspects of...
- Rendering the initial slotted loading content
- Handling the unusual case of a challenge with no component (
ShellChallenge) - Letting
renderChallengehave a hard reference to thechallengewhich invoked it, rather than a reference determined offthis.challenge
| export class StageMappingError extends TypeError { | ||
| constructor(message: string, options?: ErrorOptions) { | ||
| super(message, options); | ||
| this.name = "StageMappingError"; | ||
| } |
There was a problem hiding this comment.
I've had some luck on debugging our Docusaurus plugins with extended Error classes. The original inspiration came from your push for extended Event constructors :)
| import "#flow/sources/apple/AppleLoginInit"; | ||
| import "#flow/sources/plex/PlexLoginInit"; | ||
| import "#flow/sources/telegram/TelegramLogin"; | ||
| import "#flow/stages/FlowErrorStage"; | ||
| import "#flow/stages/FlowFrameStage"; | ||
| import "#flow/stages/RedirectStage"; |
There was a problem hiding this comment.
AFACT this is handled in the FlowExecutorStages module
| public static from(entry: StageEntry): StageMapping { | ||
| const [token, ...rest] = entry; | ||
|
|
||
| public static async from([token, ...rest]: StageEntry): Promise<StageMapping> { |
There was a problem hiding this comment.
Your suggestions about removing the cache were spot on and made error handling here a bit easier to lay out.
| if (!tag) { | ||
| throw new StageMappingError( | ||
| `Invalid stage entry for component ${token}: No tag or import callback provided.`, | ||
| ); | ||
| } |
There was a problem hiding this comment.
A highly unlikely scenario, but this'll get something human-readable for the user.
…to web/flow/tablize-token-component-relationship-v3-error-handling * web/flow/19999-tablize-token-component-relationship: (58 commits) web/admin: maintenance: centralize types that are used across stages (#20398) website/integrations: beszel: remove slug reference (#20393) web/admin: maintenance: give dialogs default exports (#20397) web: Fix element property names with custom attributes. (#20396) enterprise/providers/microsoft_entra: fix dangling comma (#20391) web/admin: bug: stage update forms not rendering, several modal form buttons missing (#20373) lifecycle: bump rac guacd base image (#20390) web: revert `tree-sitter` removal from lockfile (#20377) root: fix dependabot config for docker (#20380) website/docs: Fix broken link to flow executor (#20364) core: add cause to `ak_groups` deprecation event and logs (#20361) rbac: fix object permission request (#20304) enterprise/providers/ws_federation: fix incorrect metadata download URL (#20173) core, web: update translations (#20303) stages/authenticator_webauthn: Update FIDO MDS3 & Passkey aaguid blobs (#20305) core: bump django-countries from 7.6.1 to 8.2.0 (#19459) web: bump the storybook group across 1 directory with 5 updates (#20130) web: bump pino from 10.3.0 to 10.3.1 in /web (#20133) core: bump github.com/pires/go-proxyproto from 0.10.0 to 0.11.0 (#20182) web: bump @patternfly/elements from 4.2.0 to 4.3.1 in /web (#20185) ...
✅ Deploy Preview for authentik-docs ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
5d4537e
into
web/flow/tablize-token-component-relationship
…-to-maintain table (#19999) * web: Add InvalidationFlow to Radius Provider dialogues ## What - Bugfix: adds the InvalidationFlow to the Radius Provider dialogues - Repairs: `{"invalidation_flow":["This field is required."]}` message, which was *not* propagated to the Notification. - Nitpick: Pretties `?foo=${true}` expressions: `s/\?([^=]+)=\$\{true\}/\1/` ## Note Yes, I know I'm going to have to do more magic when we harmonize the forms, and no, I didn't add the Property Mappings to the wizard, and yes, I know I'm going to have pain with the *new* version of the wizard. But this is a serious bug; you can't make Radius servers with *either* of the current dialogues at the moment. * This (temporary) change is needed to prevent the unit tests from failing. \# What \# Why \# How \# Designs \# Test Steps \# Other Notes * Revert "This (temporary) change is needed to prevent the unit tests from failing." This reverts commit dddde09. * website: fix bad escaping of URLs in release notes ## What Fixes bad escaping of URLs in the release notes that resulted in mangled output. v2024.6.4 had entries that looked like this: ``` ##### `GET` /providers/google_workspace/{#123;id}#125;/ ``` v2025.4.md had entries that looked like this: ``` ##### `GET` /policies/unique_password/{#125;#123;policy_uuid}/ ``` A couple of straightforward search-and-replaces has fixed the issue. ## Notes Two of the release notes had bad escaping of URLs. I'm not sure how the error was made or got past, but it was obvious when visiting the page. @BeryJu suggested that the bug is due to our using `{...}` to symbolize parameters in a URL while Docusaurus wants to interpret `{...}` as an internal template instruction, resulting in odd behavior. In either case, docusarus interpreted the hashtagged entries as links to unrelated issues in Github (the same two issues, which were "bump version of pylint" and "bump version of sentry"), which could be very confusing. The inconsistencies between the two releases, and the working releases, suggests that the error was introduced manually. * web/flow: refactor FlowExecutor so that client-side stage selection is separate from stage execution # What Extracts and normalizes the *massive* switch/case statement into a table, eliminating as much repetition as possible. Where the server-side stage token and the client-side component have the same tag, only one is required. There were three different patterns for prop definitions, and those have been regularized into an expression with a compile-time type check, and the most common one can be omitted from the stage definition table. # Why 1. Because it’s hella cleaner. Stages are clear and easy to spot in the table (especially when it’s alphabetically ordered, OMG). Stages that disagree in name with their components, stages that take props different from the “standard” set, and stages that need `import` statements, are all easy to identify. 2. Because identifying what we *do* with our web components is critical to their success, and to the success of the styling system the authentik web team envisions. FlowExecutor provides selection and execution of stages, but it also provides the inspector, the locale selector, headers, footers, customizations, and branding. Clearing away clutter to make that easier to see makes future refactoring for compatibility mode and dark theme handling much easier. * web/flow: clean up state representation in FlowExecutor (#20027) * web/flow: clean up state representation in FlowExecutor # What Cleans up the state and lifecycle of FlowExecutor. *As state lifecycle*, the two fields `challenge` and `flowInfo` are synonymous: they are modified at the same time, once in the setter, and once in `updated()`; flowInfo is always a derived consequence of that current challenge. Making `challenge` the property that we are monitoring and `flowInfo` a simple accessor on `challenge` eliminates duplication of state management. Lit automatically schedules a re-render whenever `challenge` is changed; the `requestUpdate()` is therefore not needed. With that, the only thing left is where or when to change the document title. That too is moved to `updated()` and happens without checking for need; it does no harm to replace a string with its own value, the performance loss is so small as to be non-existent, it will not confuse the browser or the environment. Eliminating an `if` and reducing the code surface to a pattern check is a win. FlowExecutor now has only three states: Loading, Challenge Available, and… Inspector? Let’s see what we can do about cleaning these up as well. Loading and Challenge do not seem synonymous: the challenge should not be altered until the fetch is complete, to prevent blank displays. * web/flow: dedupe the set error flow state (#20029) * web/flow: dedupe the set error flow state # What Extracts the logic for setting the flow state to FlowError. # Why It was just duplication. Trying to clean up state management is easier when special state handling is isolated into a single method. * Protected. --------- Co-authored-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com> --------- Co-authored-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com> * Fix types. * web: Flesh out module driven tag names. * web/flow: optimize table for type safety # What Separate out the “here’s how a stage is defined” from “Here’s how a stage is represented internally.” This gives us a nice central store of where to define how the server-side componentName relates to a client-side customElementName while also guaranteeing that the componenName or supplied customElementName exists and corresponds. Type safety has been preserved system-wide (thanks, @GirlBossRush!) * Tidy. * Removed the cache; it's extra code for no benefit whatsoever; the table is constructed ONCE at start-up, there's never going to be a cache hit. The FlowExecutorStageFactory produces StageMappings (StageMapping[]), which is itself a warehouse of singular server-component -> client-component relationships, fetching the client from the bundle as needed. The StageMapping only does the fetch once per instance, so (for example) a password failure will reinstantiate a PasswordStage, but it will not fetch it a second time. * Removed comments about the cache. Added comments about where to find the FlowExecutor stage table. Moved the import of WebAuthnAuthenticticatorRegisterState from FlowExecutor.ts to FlowExecutorStages.ts; both files are bundled together, so this is a no-op functionally, but it's easier to confirm that StageEntries without import expressions (STageModuleCallbacks) have their stages bundled (pre-imported) if the import statement is in the same file. * web: Flesh out stage mapping error handling. (#20292) Co-authored-by: Ken Sternberg <ken@goauthentik.io> * Restore fallback to use token if neither tag nor import are present. * Bad check. --------- Co-authored-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>
…to web/flow/20030-one-true-api * web/flow/19999-tablize-token-component-relationship: (58 commits) web: Flesh out stage mapping error handling. (#20292) web/admin: maintenance: centralize types that are used across stages (#20398) website/integrations: beszel: remove slug reference (#20393) web/admin: maintenance: give dialogs default exports (#20397) web: Fix element property names with custom attributes. (#20396) enterprise/providers/microsoft_entra: fix dangling comma (#20391) web/admin: bug: stage update forms not rendering, several modal form buttons missing (#20373) lifecycle: bump rac guacd base image (#20390) web: revert `tree-sitter` removal from lockfile (#20377) root: fix dependabot config for docker (#20380) website/docs: Fix broken link to flow executor (#20364) core: add cause to `ak_groups` deprecation event and logs (#20361) rbac: fix object permission request (#20304) enterprise/providers/ws_federation: fix incorrect metadata download URL (#20173) core, web: update translations (#20303) stages/authenticator_webauthn: Update FIDO MDS3 & Passkey aaguid blobs (#20305) core: bump django-countries from 7.6.1 to 8.2.0 (#19459) web: bump the storybook group across 1 directory with 5 updates (#20130) web: bump pino from 10.3.0 to 10.3.1 in /web (#20133) core: bump github.com/pires/go-proxyproto from 0.10.0 to 0.11.0 (#20182) ...
…se it for the lifetime of the FlowExecutor (#20030) * web: Add InvalidationFlow to Radius Provider dialogues ## What - Bugfix: adds the InvalidationFlow to the Radius Provider dialogues - Repairs: `{"invalidation_flow":["This field is required."]}` message, which was *not* propagated to the Notification. - Nitpick: Pretties `?foo=${true}` expressions: `s/\?([^=]+)=\$\{true\}/\1/` ## Note Yes, I know I'm going to have to do more magic when we harmonize the forms, and no, I didn't add the Property Mappings to the wizard, and yes, I know I'm going to have pain with the *new* version of the wizard. But this is a serious bug; you can't make Radius servers with *either* of the current dialogues at the moment. * This (temporary) change is needed to prevent the unit tests from failing. \# What \# Why \# How \# Designs \# Test Steps \# Other Notes * Revert "This (temporary) change is needed to prevent the unit tests from failing." This reverts commit dddde09. * website: fix bad escaping of URLs in release notes ## What Fixes bad escaping of URLs in the release notes that resulted in mangled output. v2024.6.4 had entries that looked like this: ``` ##### `GET` /providers/google_workspace/{#123;id}#125;/ ``` v2025.4.md had entries that looked like this: ``` ##### `GET` /policies/unique_password/{#125;#123;policy_uuid}/ ``` A couple of straightforward search-and-replaces has fixed the issue. ## Notes Two of the release notes had bad escaping of URLs. I'm not sure how the error was made or got past, but it was obvious when visiting the page. @BeryJu suggested that the bug is due to our using `{...}` to symbolize parameters in a URL while Docusaurus wants to interpret `{...}` as an internal template instruction, resulting in odd behavior. In either case, docusarus interpreted the hashtagged entries as links to unrelated issues in Github (the same two issues, which were "bump version of pylint" and "bump version of sentry"), which could be very confusing. The inconsistencies between the two releases, and the working releases, suggests that the error was introduced manually. * web/flow: refactor FlowExecutor so that client-side stage selection is separate from stage execution # What Extracts and normalizes the *massive* switch/case statement into a table, eliminating as much repetition as possible. Where the server-side stage token and the client-side component have the same tag, only one is required. There were three different patterns for prop definitions, and those have been regularized into an expression with a compile-time type check, and the most common one can be omitted from the stage definition table. # Why 1. Because it’s hella cleaner. Stages are clear and easy to spot in the table (especially when it’s alphabetically ordered, OMG). Stages that disagree in name with their components, stages that take props different from the “standard” set, and stages that need `import` statements, are all easy to identify. 2. Because identifying what we *do* with our web components is critical to their success, and to the success of the styling system the authentik web team envisions. FlowExecutor provides selection and execution of stages, but it also provides the inspector, the locale selector, headers, footers, customizations, and branding. Clearing away clutter to make that easier to see makes future refactoring for compatibility mode and dark theme handling much easier. * web/flow: clean up state representation in FlowExecutor # What Cleans up the state and lifecycle of FlowExecutor. *As state lifecycle*, the two fields `challenge` and `flowInfo` are synonymous: they are modified at the same time, once in the setter, and once in `updated()`; flowInfo is always a derived consequence of that current challenge. Making `challenge` the property that we are monitoring and `flowInfo` a simple accessor on `challenge` eliminates duplication of state management. Lit automatically schedules a re-render whenever `challenge` is changed; the `requestUpdate()` is therefore not needed. With that, the only thing left is where or when to change the document title. That too is moved to `updated()` and happens without checking for need; it does no harm to replace a string with its own value, the performance loss is so small as to be non-existent, it will not confuse the browser or the environment. Eliminating an `if` and reducing the code surface to a pattern check is a win. FlowExecutor now has only three states: Loading, Challenge Available, and… Inspector? Let’s see what we can do about cleaning these up as well. Loading and Challenge do not seem synonymous: the challenge should not be altered until the fetch is complete, to prevent blank displays. * web/flow: dedupe the set error flow state # What Extracts the logic for setting the flow state to FlowError. # Why It was just duplication. Trying to clean up state management is easier when special state handling is isolated into a single method. * web/flow: dedupe the creation of fresh FlowApi instances # What Generates a single instance of FlowApi() that the FlowExecutor can use over the course of its lifetime. # Why Looking at the code generated by OpenApi, it’s clear that the parameters with which the API commits network transactions are immutable after construction; likewise, our particular invocation of `DEFAULT_GLOBALS` is also immutable with respect to a single instance of the FlowExecutor. With that in mind, there’s no reason to keep rebuilding the same network transaction object over and over; just instantiate it and live with it. In the conflict between rules-of-thumb “Never store what you can express” and “Extract repetitious expressions into instances,” the latter rule wins here. * web/flow: clean up state representation in FlowExecutor (#20027) * web/flow: clean up state representation in FlowExecutor # What Cleans up the state and lifecycle of FlowExecutor. *As state lifecycle*, the two fields `challenge` and `flowInfo` are synonymous: they are modified at the same time, once in the setter, and once in `updated()`; flowInfo is always a derived consequence of that current challenge. Making `challenge` the property that we are monitoring and `flowInfo` a simple accessor on `challenge` eliminates duplication of state management. Lit automatically schedules a re-render whenever `challenge` is changed; the `requestUpdate()` is therefore not needed. With that, the only thing left is where or when to change the document title. That too is moved to `updated()` and happens without checking for need; it does no harm to replace a string with its own value, the performance loss is so small as to be non-existent, it will not confuse the browser or the environment. Eliminating an `if` and reducing the code surface to a pattern check is a win. FlowExecutor now has only three states: Loading, Challenge Available, and… Inspector? Let’s see what we can do about cleaning these up as well. Loading and Challenge do not seem synonymous: the challenge should not be altered until the fetch is complete, to prevent blank displays. * web/flow: dedupe the set error flow state (#20029) * web/flow: dedupe the set error flow state # What Extracts the logic for setting the flow state to FlowError. # Why It was just duplication. Trying to clean up state management is easier when special state handling is isolated into a single method. * Protected. --------- Co-authored-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com> --------- Co-authored-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com> * Fix types. * web: Flesh out module driven tag names. * web/flow: optimize table for type safety # What Separate out the “here’s how a stage is defined” from “Here’s how a stage is represented internally.” This gives us a nice central store of where to define how the server-side componentName relates to a client-side customElementName while also guaranteeing that the componenName or supplied customElementName exists and corresponds. Type safety has been preserved system-wide (thanks, @GirlBossRush!) * Tidy. * Removed the cache; it's extra code for no benefit whatsoever; the table is constructed ONCE at start-up, there's never going to be a cache hit. The FlowExecutorStageFactory produces StageMappings (StageMapping[]), which is itself a warehouse of singular server-component -> client-component relationships, fetching the client from the bundle as needed. The StageMapping only does the fetch once per instance, so (for example) a password failure will reinstantiate a PasswordStage, but it will not fetch it a second time. * Removed comments about the cache. Added comments about where to find the FlowExecutor stage table. Moved the import of WebAuthnAuthenticticatorRegisterState from FlowExecutor.ts to FlowExecutorStages.ts; both files are bundled together, so this is a no-op functionally, but it's easier to confirm that StageEntries without import expressions (STageModuleCallbacks) have their stages bundled (pre-imported) if the import statement is in the same file. * web: Flesh out stage mapping error handling. (#20292) Co-authored-by: Ken Sternberg <ken@goauthentik.io> --------- Co-authored-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>
…t-flow-inspector * web/flow/20030-one-true-api: (58 commits) web: Flesh out stage mapping error handling. (#20292) web/admin: maintenance: centralize types that are used across stages (#20398) website/integrations: beszel: remove slug reference (#20393) web/admin: maintenance: give dialogs default exports (#20397) web: Fix element property names with custom attributes. (#20396) enterprise/providers/microsoft_entra: fix dangling comma (#20391) web/admin: bug: stage update forms not rendering, several modal form buttons missing (#20373) lifecycle: bump rac guacd base image (#20390) web: revert `tree-sitter` removal from lockfile (#20377) root: fix dependabot config for docker (#20380) website/docs: Fix broken link to flow executor (#20364) core: add cause to `ak_groups` deprecation event and logs (#20361) rbac: fix object permission request (#20304) enterprise/providers/ws_federation: fix incorrect metadata download URL (#20173) core, web: update translations (#20303) stages/authenticator_webauthn: Update FIDO MDS3 & Passkey aaguid blobs (#20305) core: bump django-countries from 7.6.1 to 8.2.0 (#19459) web: bump the storybook group across 1 directory with 5 updates (#20130) web: bump pino from 10.3.0 to 10.3.1 in /web (#20133) core: bump github.com/pires/go-proxyproto from 0.10.0 to 0.11.0 (#20182) ...
…cle (#20063) * web: Add InvalidationFlow to Radius Provider dialogues ## What - Bugfix: adds the InvalidationFlow to the Radius Provider dialogues - Repairs: `{"invalidation_flow":["This field is required."]}` message, which was *not* propagated to the Notification. - Nitpick: Pretties `?foo=${true}` expressions: `s/\?([^=]+)=\$\{true\}/\1/` ## Note Yes, I know I'm going to have to do more magic when we harmonize the forms, and no, I didn't add the Property Mappings to the wizard, and yes, I know I'm going to have pain with the *new* version of the wizard. But this is a serious bug; you can't make Radius servers with *either* of the current dialogues at the moment. * This (temporary) change is needed to prevent the unit tests from failing. \# What \# Why \# How \# Designs \# Test Steps \# Other Notes * Revert "This (temporary) change is needed to prevent the unit tests from failing." This reverts commit dddde09. * website: fix bad escaping of URLs in release notes ## What Fixes bad escaping of URLs in the release notes that resulted in mangled output. v2024.6.4 had entries that looked like this: ``` ##### `GET` /providers/google_workspace/{#123;id}#125;/ ``` v2025.4.md had entries that looked like this: ``` ##### `GET` /policies/unique_password/{#125;#123;policy_uuid}/ ``` A couple of straightforward search-and-replaces has fixed the issue. ## Notes Two of the release notes had bad escaping of URLs. I'm not sure how the error was made or got past, but it was obvious when visiting the page. @BeryJu suggested that the bug is due to our using `{...}` to symbolize parameters in a URL while Docusaurus wants to interpret `{...}` as an internal template instruction, resulting in odd behavior. In either case, docusarus interpreted the hashtagged entries as links to unrelated issues in Github (the same two issues, which were "bump version of pylint" and "bump version of sentry"), which could be very confusing. The inconsistencies between the two releases, and the working releases, suggests that the error was introduced manually. * web/flow: refactor FlowExecutor so that client-side stage selection is separate from stage execution # What Extracts and normalizes the *massive* switch/case statement into a table, eliminating as much repetition as possible. Where the server-side stage token and the client-side component have the same tag, only one is required. There were three different patterns for prop definitions, and those have been regularized into an expression with a compile-time type check, and the most common one can be omitted from the stage definition table. # Why 1. Because it’s hella cleaner. Stages are clear and easy to spot in the table (especially when it’s alphabetically ordered, OMG). Stages that disagree in name with their components, stages that take props different from the “standard” set, and stages that need `import` statements, are all easy to identify. 2. Because identifying what we *do* with our web components is critical to their success, and to the success of the styling system the authentik web team envisions. FlowExecutor provides selection and execution of stages, but it also provides the inspector, the locale selector, headers, footers, customizations, and branding. Clearing away clutter to make that easier to see makes future refactoring for compatibility mode and dark theme handling much easier. * web/flow: clean up state representation in FlowExecutor # What Cleans up the state and lifecycle of FlowExecutor. *As state lifecycle*, the two fields `challenge` and `flowInfo` are synonymous: they are modified at the same time, once in the setter, and once in `updated()`; flowInfo is always a derived consequence of that current challenge. Making `challenge` the property that we are monitoring and `flowInfo` a simple accessor on `challenge` eliminates duplication of state management. Lit automatically schedules a re-render whenever `challenge` is changed; the `requestUpdate()` is therefore not needed. With that, the only thing left is where or when to change the document title. That too is moved to `updated()` and happens without checking for need; it does no harm to replace a string with its own value, the performance loss is so small as to be non-existent, it will not confuse the browser or the environment. Eliminating an `if` and reducing the code surface to a pattern check is a win. FlowExecutor now has only three states: Loading, Challenge Available, and… Inspector? Let’s see what we can do about cleaning these up as well. Loading and Challenge do not seem synonymous: the challenge should not be altered until the fetch is complete, to prevent blank displays. * web/flow: dedupe the set error flow state # What Extracts the logic for setting the flow state to FlowError. # Why It was just duplication. Trying to clean up state management is easier when special state handling is isolated into a single method. * web/flow: dedupe the creation of fresh FlowApi instances # What Generates a single instance of FlowApi() that the FlowExecutor can use over the course of its lifetime. # Why Looking at the code generated by OpenApi, it’s clear that the parameters with which the API commits network transactions are immutable after construction; likewise, our particular invocation of `DEFAULT_GLOBALS` is also immutable with respect to a single instance of the FlowExecutor. With that in mind, there’s no reason to keep rebuilding the same network transaction object over and over; just instantiate it and live with it. In the conflict between rules-of-thumb “Never store what you can express” and “Extract repetitious expressions into instances,” the latter rule wins here. * Intermediate. Gonna check against results. * web/flow: extract inspector into standalone lifecycle # What Removes all of the code from `FlowExecutor` related to the inspector and isolates it into its own component. The lifecycle of FlowExecutor’s inspector handling has been adjusted to maintain the existing behavior. # How FlowExecutor is reduced to merely presenting the button: - In `FlowExecutor`: - Remove all the controls and references to FlowInspector - Remove the capabilities check - Remove the inspector listener - Remove the render guards - Remove the “inspect” PropVariant (and remove it from `FlowExecutorSelections`) - Remove the inspector toggle - Remove the inspector renderer - Always dispatch FlowAdvance events (if the inspector is not present they will be ignored) - Adjust `ak-stage-redirect` to not take “promptUser” as an attribute - Replace the whole render-inspector-button clause with `ak-flow-inspector-button` - Adjust CSS to use `ak-flow-inspector-button` instead of `.inspector-button` RedirectStage now queries the context for inspector availability and state: - In `stages/RedirectStage`: - Change `promptUser` from a property accessor to a simple accessor that queries the parent context for the inspector state - Remove the `@property` clause FlowInspectorButton takes over these responsibilities, isolating this separate concern into a single file: - Manages loaded, available, and open states - Does the capabilities check - Listens for FlowInspectorChangeEvents on the window object - Renders nothing at all if the inspector is inaccessible or if the inspector is present and covering up the button - On connectedCallback checks if the URL indicate the inspector should already be open - Manages loading the FlowInspector on demand and toggles the drawer on state change To my great surprise, `FlowInspector` itself required no changes. * web/flow: clean up state representation in FlowExecutor (#20027) * web/flow: clean up state representation in FlowExecutor # What Cleans up the state and lifecycle of FlowExecutor. *As state lifecycle*, the two fields `challenge` and `flowInfo` are synonymous: they are modified at the same time, once in the setter, and once in `updated()`; flowInfo is always a derived consequence of that current challenge. Making `challenge` the property that we are monitoring and `flowInfo` a simple accessor on `challenge` eliminates duplication of state management. Lit automatically schedules a re-render whenever `challenge` is changed; the `requestUpdate()` is therefore not needed. With that, the only thing left is where or when to change the document title. That too is moved to `updated()` and happens without checking for need; it does no harm to replace a string with its own value, the performance loss is so small as to be non-existent, it will not confuse the browser or the environment. Eliminating an `if` and reducing the code surface to a pattern check is a win. FlowExecutor now has only three states: Loading, Challenge Available, and… Inspector? Let’s see what we can do about cleaning these up as well. Loading and Challenge do not seem synonymous: the challenge should not be altered until the fetch is complete, to prevent blank displays. * web/flow: dedupe the set error flow state (#20029) * web/flow: dedupe the set error flow state # What Extracts the logic for setting the flow state to FlowError. # Why It was just duplication. Trying to clean up state management is easier when special state handling is isolated into a single method. * Protected. --------- Co-authored-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com> --------- Co-authored-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com> * Fix types. * web: Flesh out module driven tag names. * web/flow: optimize table for type safety # What Separate out the “here’s how a stage is defined” from “Here’s how a stage is represented internally.” This gives us a nice central store of where to define how the server-side componentName relates to a client-side customElementName while also guaranteeing that the componenName or supplied customElementName exists and corresponds. Type safety has been preserved system-wide (thanks, @GirlBossRush!) * Prettier is still having opinions. * Tidy. * Removed the cache; it's extra code for no benefit whatsoever; the table is constructed ONCE at start-up, there's never going to be a cache hit. The FlowExecutorStageFactory produces StageMappings (StageMapping[]), which is itself a warehouse of singular server-component -> client-component relationships, fetching the client from the bundle as needed. The StageMapping only does the fetch once per instance, so (for example) a password failure will reinstantiate a PasswordStage, but it will not fetch it a second time. * Removed comments about the cache. Added comments about where to find the FlowExecutor stage table. Moved the import of WebAuthnAuthenticticatorRegisterState from FlowExecutor.ts to FlowExecutorStages.ts; both files are bundled together, so this is a no-op functionally, but it's easier to confirm that StageEntries without import expressions (STageModuleCallbacks) have their stages bundled (pre-imported) if the import statement is in the same file. * Of COURSE prettier had opinions! * Since the check for `this.can(CapabilitiesEnum.CanDebug))` has been moved into the FlowInspectorButton, FlowExecutor no longer needs the capabilities check at all. * Move the inspector into its own folder. * web: Flesh out stage mapping error handling. (#20292) Co-authored-by: Ken Sternberg <ken@goauthentik.io> * Weird merge bug: same function appeared twice. --------- Co-authored-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>
…dy-identification-stage * web/flow/20063-extract-flow-inspector: Weird merge bug: same function appeared twice. web: Flesh out stage mapping error handling. (#20292)
* web: Add InvalidationFlow to Radius Provider dialogues
## What
- Bugfix: adds the InvalidationFlow to the Radius Provider dialogues
- Repairs: `{"invalidation_flow":["This field is required."]}` message, which was *not* propagated
to the Notification.
- Nitpick: Pretties `?foo=${true}` expressions: `s/\?([^=]+)=\$\{true\}/\1/`
## Note
Yes, I know I'm going to have to do more magic when we harmonize the forms, and no, I didn't add the
Property Mappings to the wizard, and yes, I know I'm going to have pain with the *new* version of
the wizard. But this is a serious bug; you can't make Radius servers with *either* of the current
dialogues at the moment.
* This (temporary) change is needed to prevent the unit tests from failing.
\# What
\# Why
\# How
\# Designs
\# Test Steps
\# Other Notes
* Revert "This (temporary) change is needed to prevent the unit tests from failing."
This reverts commit dddde09.
* website: fix bad escaping of URLs in release notes
## What
Fixes bad escaping of URLs in the release notes that resulted in mangled output.
v2024.6.4 had entries that looked like this:
```
##### `GET` /providers/google_workspace/{#123;id}#125;/
```
v2025.4.md had entries that looked like this:
```
##### `GET` /policies/unique_password/{#125;#123;policy_uuid}/
```
A couple of straightforward search-and-replaces has fixed the issue.
## Notes
Two of the release notes had bad escaping of URLs. I'm not sure how the error was made or got past,
but it was obvious when visiting the page.
@BeryJu suggested that the bug is due to our using `{...}` to symbolize parameters in a URL while
Docusaurus wants to interpret `{...}` as an internal template instruction, resulting in odd
behavior. In either case, docusarus interpreted the hashtagged entries as links to unrelated issues
in Github (the same two issues, which were "bump version of pylint" and "bump version of sentry"),
which could be very confusing.
The inconsistencies between the two releases, and the working releases, suggests that the error was
introduced manually.
* web/flow: refactor FlowExecutor so that client-side stage selection is separate from stage execution
# What
Extracts and normalizes the *massive* switch/case statement into a table, eliminating as much repetition as possible. Where the server-side stage token and the client-side component have the same tag, only one is required. There were three different patterns for prop definitions, and those have been regularized into an expression with a compile-time type check, and the most common one can be omitted from the stage definition table.
# Why
1. Because it’s hella cleaner. Stages are clear and easy to spot in the table (especially when it’s alphabetically ordered, OMG). Stages that disagree in name with their components, stages that take props different from the “standard” set, and stages that need `import` statements, are all easy to identify.
2. Because identifying what we *do* with our web components is critical to their success, and to the success of the styling system the authentik web team envisions. FlowExecutor provides selection and execution of stages, but it also provides the inspector, the locale selector, headers, footers, customizations, and branding. Clearing away clutter to make that easier to see makes future refactoring for compatibility mode and dark theme handling much easier.
* web/flow: clean up state representation in FlowExecutor
# What
Cleans up the state and lifecycle of FlowExecutor.
*As state lifecycle*, the two fields `challenge` and `flowInfo` are synonymous: they are modified at the same time, once in the setter, and once in `updated()`; flowInfo is always a derived consequence of that current challenge. Making `challenge` the property that we are monitoring and `flowInfo` a simple accessor on `challenge` eliminates duplication of state management.
Lit automatically schedules a re-render whenever `challenge` is changed; the `requestUpdate()` is therefore not needed.
With that, the only thing left is where or when to change the document title. That too is moved to `updated()` and happens without checking for need; it does no harm to replace a string with its own value, the performance loss is so small as to be non-existent, it will not confuse the browser or the environment. Eliminating an `if` and reducing the code surface to a pattern check is a win.
FlowExecutor now has only three states: Loading, Challenge Available, and… Inspector? Let’s see what we can do about cleaning these up as well. Loading and Challenge do not seem synonymous: the challenge should not be altered until the fetch is complete, to prevent blank displays.
* web/flow: dedupe the set error flow state
# What
Extracts the logic for setting the flow state to FlowError.
# Why
It was just duplication. Trying to clean up state management is easier when special state handling is isolated into a single method.
* web/flow: dedupe the creation of fresh FlowApi instances
# What
Generates a single instance of FlowApi() that the FlowExecutor can use over the course of its lifetime.
# Why
Looking at the code generated by OpenApi, it’s clear that the parameters with which the API commits network transactions are immutable after construction; likewise, our particular invocation of `DEFAULT_GLOBALS` is also immutable with respect to a single instance of the FlowExecutor. With that in mind, there’s no reason to keep rebuilding the same network transaction object over and over; just instantiate it and live with it. In the conflict between rules-of-thumb “Never store what you can express” and “Extract repetitious expressions into instances,” the latter rule wins here.
* Intermediate. Gonna check against results.
* web/flow: extract inspector into standalone lifecycle
# What
Removes all of the code from `FlowExecutor` related to the inspector and isolates it into its own component. The lifecycle of FlowExecutor’s inspector handling has been adjusted to maintain the existing behavior.
# How
FlowExecutor is reduced to merely presenting the button:
- In `FlowExecutor`:
- Remove all the controls and references to FlowInspector
- Remove the capabilities check
- Remove the inspector listener
- Remove the render guards
- Remove the “inspect” PropVariant (and remove it from `FlowExecutorSelections`)
- Remove the inspector toggle
- Remove the inspector renderer
- Always dispatch FlowAdvance events (if the inspector is not present they will be ignored)
- Adjust `ak-stage-redirect` to not take “promptUser” as an attribute
- Replace the whole render-inspector-button clause with `ak-flow-inspector-button`
- Adjust CSS to use `ak-flow-inspector-button` instead of `.inspector-button`
RedirectStage now queries the context for inspector availability and state:
- In `stages/RedirectStage`:
- Change `promptUser` from a property accessor to a simple accessor that queries the parent context for the inspector state
- Remove the `@property` clause
FlowInspectorButton takes over these responsibilities, isolating this separate concern into a single file:
- Manages loaded, available, and open states
- Does the capabilities check
- Listens for FlowInspectorChangeEvents on the window object
- Renders nothing at all if the inspector is inaccessible or if the inspector is present and covering up the button
- On connectedCallback checks if the URL indicate the inspector should already be open
- Manages loading the FlowInspector on demand and toggles the drawer on state change
To my great surprise, `FlowInspector` itself required no changes.
* Initial experiment to move stages into the light.
* web/flow: clean up state representation in FlowExecutor (#20027)
* web/flow: clean up state representation in FlowExecutor
# What
Cleans up the state and lifecycle of FlowExecutor.
*As state lifecycle*, the two fields `challenge` and `flowInfo` are synonymous: they are modified at the same time, once in the setter, and once in `updated()`; flowInfo is always a derived consequence of that current challenge. Making `challenge` the property that we are monitoring and `flowInfo` a simple accessor on `challenge` eliminates duplication of state management.
Lit automatically schedules a re-render whenever `challenge` is changed; the `requestUpdate()` is therefore not needed.
With that, the only thing left is where or when to change the document title. That too is moved to `updated()` and happens without checking for need; it does no harm to replace a string with its own value, the performance loss is so small as to be non-existent, it will not confuse the browser or the environment. Eliminating an `if` and reducing the code surface to a pattern check is a win.
FlowExecutor now has only three states: Loading, Challenge Available, and… Inspector? Let’s see what we can do about cleaning these up as well. Loading and Challenge do not seem synonymous: the challenge should not be altered until the fetch is complete, to prevent blank displays.
* web/flow: dedupe the set error flow state (#20029)
* web/flow: dedupe the set error flow state
# What
Extracts the logic for setting the flow state to FlowError.
# Why
It was just duplication. Trying to clean up state management is easier when special state handling is isolated into a single method.
* Protected.
---------
Co-authored-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>
---------
Co-authored-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>
* Fix types.
* web: Flesh out module driven tag names.
* Experiment continues: first-tier into the light.
* web/flow: optimize table for type safety
# What
Separate out the “here’s how a stage is defined” from “Here’s how a stage is represented internally.” This gives us a nice central store of where to define how the server-side componentName relates to a client-side customElementName while also guaranteeing that the componenName or supplied customElementName exists and corresponds. Type safety has been preserved system-wide (thanks, @GirlBossRush!)
* Prettier is still having opinions.
* web/flow: re-arrange IdentificationStage for maintainability
# What
Every conditional section of the IdentificationStage has been separated out into its own individual render function. Where possible, the information passed to the renderer has been reduced to a bare minimum (i.e if the function only needed the `passwordlessUrl`, that’s the only thing that’s passed to it), which helps highlight some inconsistencies in the API.
# No change
This is a purely maintenance-level change to the code, to make it obvious what needs to be plumbed/corrected in order to expose our dialogs to password managers. No functionality has been changed.
# Why
Figuring out how to turn our web components into proper elements, where what they contain is not isolated from the view of password managers, requires pulling out the functionality into small, readable components.
# Future work
Doing this has exposed several fundamental issues:
- auto-redirect is a state change from one LoginChallenge to another under a collection of conditions available on the challenge, triggered when FlowExecutor writes a new challenge. “Which challenge?” in FlowExecutor ought to be handling this, not handing it off to IdentificationStage.
- Everything about Captcha is about Captcha. It ought to be in its own little state managing class, perhaps as a lit controller.
- The same is true about WebAuthn.
- `host` is doing very little work; at best, it’s receiving a “change this” or “submit that” message, which is an Event. Look forward to that.
* Tidy.
* Removed the cache; it's extra code for no benefit whatsoever; the table is constructed ONCE at start-up, there's never going to be a cache hit. The FlowExecutorStageFactory produces StageMappings (StageMapping[]), which is itself a warehouse of singular server-component -> client-component relationships, fetching the client from the bundle as needed. The StageMapping only does the fetch once per instance, so (for example) a password failure will reinstantiate a PasswordStage, but it will not fetch it a second time.
* Removed comments about the cache. Added comments about where to find the FlowExecutor stage table. Moved the import of WebAuthnAuthenticticatorRegisterState from FlowExecutor.ts to FlowExecutorStages.ts; both files are bundled together, so this is a no-op functionally, but it's easier to confirm that StageEntries without import expressions (STageModuleCallbacks) have their stages bundled (pre-imported) if the import statement is in the same file.
* Of COURSE prettier had opinions!
* Since the check for `this.can(CapabilitiesEnum.CanDebug))` has been moved into the FlowInspectorButton, FlowExecutor no longer needs the capabilities check at all.
* Move the inspector into its own folder.
* web: Flesh out stage mapping error handling. (#20292)
Co-authored-by: Ken Sternberg <ken@goauthentik.io>
* Weird merge bug: same function appeared twice.
* Added some visibility keys, as per @GirlBossRush
---------
Co-authored-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>


Details
This PR is a follow up on #20083, displaying runtime errors that might occur during the flow executor's
renderChallengemethod.