Repository: goauthentik/authentik
File: web/src/flow/stages/identification/IdentificationStage.ts (TypeScript)

Code before:

    renderIdentityInput(challenge: IdentificationChallenge) {
        const fields = (challenge.userFields || []).sort();
        const type =
            fields.includes(UserFieldsEnum.Email) && fields.length === 1 ? "email" : "text";
        const label = OR_LIST_FORMATTERS.format(fields.map((f) => UI_FIELDS[f]));
        const passkeyChallenge = (challenge as PasskeyChallenge)?.passkeyChallenge;
        const autocomplete: AutoFill = passkeyChallenge ? "username webauthn" : "username";

        return html`<div class="pf-c-form__group">
            ${AKLabel({ required: true, htmlFor: this.inputID }, label)}
            <input
                id=${this.inputID}
                type=${type}
                name="uidField"
                placeholder=${label}
                autofocus
                autocomplete=${autocomplete}
                spellcheck="false"
                class="pf-c-form-control"
                value=${this.#rememberMe?.username ?? challenge.pendingUserIdentifier ?? ""}
                required
            />
            ${this.#rememberMe.render()}
            ${AKFormErrors({ errors: challenge.responseErrors?.uid_field })}
        </div>`;
    }

    renderPasswordFields(challenge: IdentificationChallenge) {
        return html`
            <ak-flow-input-password
                label=${msg("Password")}
                input-id="ak-stage-identification-password"
                required
                class="pf-c-form__group"
                .errors=${challenge.responseErrors?.password}
                ?allow-show-password=${challenge.allowShowPassword}
                prefill=${PasswordManagerPrefill.password ?? ""}
            ></ak-flow-input-password>
        `;
    }

    renderCaptchaStage(captchaChallenge: CaptchaChallenge) {
        return html` <div class="captcha-container">
            <ak-stage-captcha
                .challenge=${captchaChallenge}
                .onTokenChange=${this.#tokenChangeListener}
                .onLoad=${this.#captchaLoadListener}
                .refreshedAt=${this.captchaRefreshedAt}
                embedded
            >

Review comment (security):
```suggestion
return guard([challenge], () => {
            const fields = (challenge.userFields || []).sort();
            const type =
                fields.includes(UserFieldsEnum.Email) && fields.length === 1 ? "email" : "text";
            const label = OR_LIST_FORMATTERS.format(fields.map((f) => UI_FIELDS[f]));
            const passkeyChallenge = (challenge as PasskeyChallenge)?.passkeyChallenge;
            const autocomplete: AutoFill = passkeyChallenge ? "username webauthn" : "username";

            return html`<div class="pf-c-form__group">
                ${AKLabel({ required: true, htmlFor: this.inputID }, label)}
                <input
                    id=${this.inputID}
                    type=${type}
                    name="uidField"
                    placeholder=${label}
                    autofocus
                    autocomplete=${autocomplete}
                    spellcheck="false"
                    class="pf-c-form-control"
                    value=${this.#rememberMe?.username ?? challenge.pendingUserIdentifier ?? ""}
                    required
                />
                ${this.#rememberMe.render()}
                ${AKFormErrors({ errors: challenge.responseErrors?.uid_field })}
            </div>`;
        });
```

Code after:
                class="pf-c-form__group"
                .errors=${challenge.responseErrors?.password}
                ?allow-show-password=${allowShowPassword}
                prefill=${PasswordManagerPrefill.password ?? ""}
            ></ak-flow-input-password>
        `;
    }

    protected renderCaptcha(captchaChallenge: CaptchaChallenge) {
        return html`
            <div class="captcha-container">
                <ak-stage-captcha
                    .challenge=${captchaChallenge}
                    .onTokenChange=${this.#tokenChangeListener}
                    .onLoad=${this.#captchaLoadListener}
                    .refreshedAt=${this.captchaRefreshedAt}
                    embedded
                >
                </ak-stage-captcha>
                <input
                    aria-hidden="true"
                    class="faux-input"
                    ${ref(this.#captchaInputRef)}
                    name="captchaToken"
                    type="text"
                    required
                    value=""
                />
            </div>
        `;
    }

    protected renderInput(challenge: IdentificationChallenge) {
        const {
            captchaStage,
            flowDesignation,
            passkeyChallenge,
            passwordFields,
            passwordlessUrl,
            pendingUserIdentifier,
            primaryAction,
            userFields,
        } = challenge as PasskeyChallenge;

        const fields = (userFields || []).sort();
        if (fields.length === 0) {
            return html`<p>${msg("Select one of the options below to continue.")}</p>`;
        }

        const { inputID, rememberMe, captchaLoaded } = this;