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

Code before:
                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
            >
            </ak-stage-captcha>
            <input
                aria-hidden="true"
                class="faux-input"
                ${ref(this.#captchaInputRef)}
                name="captchaToken"
                type="text"
                required
                value=""
            />
        </div>`;
    }

    renderSubmitButton(challenge: IdentificationChallenge) {
        return html` <div class="pf-c-form__group ${challenge.captchaStage ? "" : "pf-m-action"}">
            <button
                ?disabled=${challenge.captchaStage &&

Review comment (suggestion):
```suggestion
    protected renderCaptchaStage(captchaChallenge: CaptchaChallenge): SlottedTemplateResult {
```

Code after:
                >
                </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;

        const offerRecovery = flowDesignation === FlowDesignationEnum.Recovery;
        const type = fields.length === 1 && fields[0] === UserFieldsEnum.Email ? "email" : "text";
        const label = OR_LIST_FORMATTERS.format(fields.map((f) => UI_FIELDS[f]));
        const username = rememberMe.username ?? pendingUserIdentifier;
        const captchaPending = captchaStage && captchaStage.interactive && !captchaLoaded;

        // When passkey is enabled, add "webauthn" to autocomplete to enable passkey autofill
        const autocomplete: AutoFill = passkeyChallenge ? "username webauthn" : "username";

        return html`${offerRecovery ? this.renderRecoveryMessage() : nothing}
            <div class="pf-c-form__group">
                ${AKLabel({ required: true, htmlFor: inputID }, label)}
                ${this.renderUidField(inputID, type, label, username, autocomplete)}
                ${rememberMe.render()}
                ${AKFormErrors({ errors: challenge.responseErrors?.uid_field })}
            </div>
            ${passwordFields ? this.renderPasswordFields(challenge) : nothing}