Files
authentik/web/src/components/ak-secret-text-input.ts
T
Ken Sternberg 52674afa8a web/fix: clarify some secrets remain secret (#23132)
* ## What

         window.authentik.flow = {
             "layout": "{{ flow.layout }}",
    +        "background": "{{ flow.background }}",
    +        "title": "{{ flow.title }}",
         };

Amends the `flow.html` template and `GlobalAuthentik` parser to include new parameters, `background` and `title`, in the flow-specific part of the configuration written to the HTML `<head>` object, and to provide those parameters to client code.

## Why

The `layout` is start-up critical: it tells the Flow interface how the admin wants the Flow page to look, and allows the HTML and CSS to be pre-aligned to that condition. `layout` is determined on a per-Flow bases, not a per-Stage basis; Flows are derived from a tuple of `(Brand, Application?)`, where the opening policy *may* direct a user to a different flow if the user reached authentik via a redirect from a specific application, but will otherwise fall back to the default Flow for the Brand.

The `background` is a field that is required if the `Flow`’s layout is of type `frame_background`; in this case, the part of the viewport not dedicated to the FlowExecutor is reserved for an `<iframe>` that will be filled in with whatever the administrator specifies. Although this gives it the same priority as `layout` (whether it’s provided or undefined) for describing the [chrome](https://developer.mozilla.org/en-US/docs/Glossary/Chrome) around a challenge, it is currently not provided to the application in the start-up config; it is provided in the `challenge` and renders the IFrame as part of the initial challenge.

This patch fixes that; if `layout` is provided, `background` ought to be as well, even if it’s empty. The execution of a Challenge ought not have any influence over the look and feel of the Flow-defined appearance *around* that Challenge.

I have added `title` as well; with that, all of the current theme-and-appearance related configuration details are placed into `<head>` and can be removed from the FlowExecutor.

Server-side, `background` is currently specified: `background = FileField(blank=True, default="")` which is … interesting since we also appear to store URLs in it. I don’t see anything in the FlowSerializer that would change that from a client’s point of view.

This patch furthers the effort to separate flow execution from flow presentation.

- \[🐰\] The code has been formatted (`make web`)

* web/update: update `secret text` to enable password-like inputs

# What

Adds a flag to ak-secret-text-input so that most secret texts are more password-like than plain-text-with-hidden. plain-text-with-hidden can still be enabled.

# Why

Some customers were uncomfortable with fields named “password” showing input as plain text during object creation.

* web/update: update `secret text` to enable password-like inputs

# What

Adds a flag to ak-secret-text-input so that most secret texts are more password-like than plain-text-with-hidden. plain-text-with-hidden can still be enabled.

# Why

Some customers were uncomfortable with fields named “password” showing input as plain text during object creation.
2026-06-16 10:05:49 -07:00

127 lines
3.9 KiB
TypeScript

import { HorizontalLightComponent } from "./HorizontalLightComponent.js";
import { ifPresent } from "#elements/utils/attributes";
import { msg } from "@lit/localize";
import { html, PropertyValues } from "lit";
import { customElement, property } from "lit/decorators.js";
import { classMap } from "lit/directives/class-map.js";
import { ifDefined } from "lit/directives/if-defined.js";
import { createRef, ref } from "lit/directives/ref.js";
import { styleMap } from "lit/directives/style-map.js";
@customElement("ak-secret-text-input")
export class AkSecretTextInput extends HorizontalLightComponent<string> {
@property({ type: String })
public value = "";
@property({ type: Boolean, reflect: true })
public revealed = false;
@property({ type: Boolean, reflect: true })
public plaintext = false;
@property({ type: String })
public placeholder = "";
@property({ type: Number, attribute: "maxlength" })
public maxLength?: number;
@property({ type: Number, attribute: "minlength" })
public minLength?: number;
public reveal = () => {
this.revealed = true;
};
#inputListener = (ev: InputEvent) => {
this.value = (ev.target as HTMLInputElement).value;
};
#ref = createRef<HTMLInputElement>();
public override updated(changedProperties: PropertyValues<this>): void {
super.updated(changedProperties);
if (changedProperties.has("revealed") && this.revealed) {
this.#ref.value?.focus();
}
}
#renderSecretInput() {
return html`<div
class="pf-c-form__horizontal-group"
style=${styleMap({
display: "flex",
gap: "var(--pf-global--spacer--sm)",
})}
>
<input
@click=${this.reveal}
id=${this.fieldID}
aria-describedby=${this.helpID}
class="pf-c-form-control"
type="password"
disabled
data-form-ignore="true"
value="**************"
/>
<input
type="text"
value="${ifDefined(this.value)}"
?required=${this.required}
name=${ifDefined(this.name)}
hidden
/>
<button
id=${this.helpID}
class="pf-c-button pf-m-tertiary pf-m-inline"
type="button"
@click=${this.reveal}
>
${msg("Modify", {
id: "ak-secret-text-input.actions.modify",
desc: "Help text for secret input field to indicate that clicking will allow changing the value.",
})}
</button>
</div>`;
}
protected renderVisibleInput() {
const code = this.inputHint === "code";
const classes = {
"pf-c-form-control": true,
"pf-m-monospace": code,
};
return html`<input
${ref(this.#ref)}
type=${this.plaintext ? "text" : "password"}
id=${this.fieldID}
aria-describedby=${this.helpID}
@input=${this.#inputListener}
name=${ifDefined(this.name)}
value=${ifDefined(this.value)}
class="${classMap(classes)}"
maxlength=${ifPresent(this.maxLength)}
minlength=${ifPresent(this.minLength)}
placeholder=${ifPresent(this.placeholder)}
autocomplete=${ifDefined(code ? "off" : undefined)}
spellcheck=${ifDefined(code ? "false" : undefined)}
?required=${this.required}
/>`;
}
public override renderControl() {
return this.revealed ? this.renderVisibleInput() : this.#renderSecretInput();
}
}
export default AkSecretTextInput;
declare global {
interface HTMLElementTagNameMap {
"ak-secret-text-input": AkSecretTextInput;
}
}