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

Code before:
    }

    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 &&
                challenge.captchaStage.interactive &&
                !this.captchaLoaded}
                type="submit"
                class="pf-c-button pf-m-primary pf-m-block"
            >
                ${challenge.primaryAction}
            </button>
        </div>`;
    }

    renderInput(challenge: IdentificationChallenge): TemplateResult {
        if (!challenge.userFields || challenge.userFields.length === 0) {
            return html`<p>${msg("Select one of the options below to continue.")}</p>`;
        }
        const { passwordFields, captchaStage } = challenge;
        const recovery = challenge.flowDesignation === FlowDesignationEnum.Recovery;

        // prettier-ignore
        return html`
            ${recovery ? this.renderRecoveryPhase() : nothing}
            ${this.renderIdentityInput(challenge)}
            ${passwordFields ? this.renderPasswordFields(challenge) : nothing}
            ${this.renderNonFieldErrors()}

Review comment (suggestion):
```suggestion
        return html`<div class="pf-c-form__group ${challenge.captchaStage ? "" : "pf-m-action"}">
```

Code after:
            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}
            ${this.renderNonFieldErrors()}
            ${captchaStage ? this.renderCaptcha(captchaStage) : nothing}

            <div class="pf-c-form__group ${captchaStage ? "" : "pf-m-action"}">
                <button
                    ?disabled=${captchaPending}
                    type="submit"
                    class="pf-c-button pf-m-primary pf-m-block"
                >
                    ${primaryAction}
                </button>
            </div>
            ${passwordlessUrl ? html`<ak-divider>${msg("Or")}</ak-divider>` : nothing}`;
    }

    protected renderPrelude(prelude: string) {
        return html`<p>${msg(str`Log in to continue to ${prelude}.`)}</p>`;
    }

    protected renderPasswordlessUrl(url: string) {
        return html`<a
            href=${url}
            class="pf-c-button pf-m-secondary pf-m-block"