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

Code before:
                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()}
            ${captchaStage ? this.renderCaptchaStage(captchaStage) : nothing}
            ${this.renderSubmitButton(challenge)}
        `;
    }

    renderPasswordlessUrl(passwordlessUrl: string) {
        return html`<ak-divider>${msg("Or")}</ak-divider>
            <a
                name="passwordless"
                href=${passwordlessUrl}
                class="pf-c-button pf-m-secondary pf-m-block"
            >
                ${msg("Use a security key")}
            </a> `;
    }

    // These have the same type, and can be supplied out-of-order. Passing them in by name prevents
    // mis-ordering.
    renderFooter({ enrollUrl, recoveryUrl }: IdentificationFooter) {
        if (!(enrollUrl || recoveryUrl)) {
            return nothing;
        }

        return html`<fieldset
            slot="footer-band"
            part="additional-actions"
            class="pf-c-login__main-footer-band"
        >
            <legend class="sr-only">${msg("Additional actions")}</legend>
            ${enrollUrl
                ? html`<div class="pf-c-login__main-footer-band-item">

Review comment (suggestion):
```suggestion
    protected renderPasswordlessUrl(passwordlessUrl: string): SlottedTemplateResult {
```

Code after:
            <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"
            ouiaId="passwordless"
        >
            ${msg("Use a security key")}
        </a> `;
    }

    //#region Render
    protected renderDefaultSource(source: LoginSource, showLabels: boolean) {
        const { name, iconUrl, challenge } = source;

        const icon = renderSourceIcon(name, iconUrl);
        return html`<button
            type="button"
            @click=${() => this.#dispatchChallengeToHost(challenge)}
            part="source-item"
            name=${`source-${kebabCase(name)}`}
            class="pf-c-button source-button"
            aria-label=${msg(str`Continue with ${name}`)}
        >
            <span class="pf-c-button__icon pf-m-start">${icon}</span>
            ${showLabels ? name : ""}
        </button>`;
    }

    protected renderPromotedSource(source: LoginSource) {
        const { name, challenge } = source;

        return html`<button
            type="button"
            @click=${() => this.#dispatchChallengeToHost(challenge)}
            part="source-item source-item-promoted"