Repository: goauthentik/authentik
File: web/src/flow/stages/authenticator_totp/AuthenticatorTOTPStage.ts (TypeScript)

Code before:
        return html`<ak-flow-card .challenge=${this.challenge}>
            <form class="pf-c-form" @submit=${this.submitForm}>
                ${FlowUserDetails({ challenge: this.challenge })}

                <input type="hidden" name="otp_uri" value=${this.challenge.configUrl} />

                <div class="pf-c-form__group">
                    <div class="qr-container">
                        <qr-code
                            role="img"
                            aria-label=${msg("QR-Code to setup a time-based one-time password")}
                            format="svg"
                            data="${this.challenge.configUrl}"
                        ></qr-code>
                        <button
                            type="button"
                            class="pf-c-button pf-m-secondary pf-m-progress pf-m-in-progress"
                            aria-label=${msg("Copy time-based one-time password configuration")}
                            @click=${(e: Event) => {
                                e.preventDefault();
                                if (!this.challenge?.configUrl) return;
                                if (!navigator.clipboard) {
                                    showMessage({
                                        level: MessageLevel.info,
                                        message: this.challenge?.configUrl,
                                    });
                                    return;
                                }
                                navigator.clipboard
                                    .writeText(this.challenge?.configUrl)
                                    .then(() => {
                                        showMessage(
                                            {
                                                level: MessageLevel.success,
                                                message: msg("Successfully copied TOTP Config."),
                                            },
                                            true,
                                        );
                                    });
                            }}
                        >
                            <span class="pf-c-button__progress"
                                ><i class="fas fa-copy" aria-hidden="true"></i
                            ></span>
                            ${msg("Copy TOTP Config")}
                        </button>
                    </div>
                </div>
                <p>
                    ${msg(
                        "Please scan the QR code above using the Microsoft Authenticator, Google Authenticator, or other authenticator apps on your device, and enter the code the device displays below to finish setting up the MFA device.",

Review comment (security):
```suggestion
    get #secretParam(): string | null {
        if (!this.challenge?.configUrl) return null;
        
        const url = this.challenge?.configUrl && URL.canParse(new URL(this.challenge.configUrl);
        
        return url?.searchParams.get("secret");
    }
```

Code after:
    ];

    private copyToClipboard(value: string, successMessage: string): void {
        if (!navigator.clipboard) {
            showMessage({
                level: MessageLevel.info,
                message: value,
            });
            return;
        }
        navigator.clipboard.writeText(value).then(() => {
            showMessage(
                {
                    level: MessageLevel.success,
                    message: successMessage,
                },
                true,
            );
        });
    }

    private getSecret(): string | null {
        if (!this.challenge?.configUrl) return null;
        const url = new URL(this.challenge.configUrl);
        return url.searchParams.get("secret");
    }

    protected render(): SlottedTemplateResult {
        if (!this.challenge) {
            return nothing;
        }

        return html`<ak-flow-card .challenge=${this.challenge}>
            <form class="pf-c-form" @submit=${this.submitForm}>
                ${FlowUserDetails({ challenge: this.challenge })}

                <input type="hidden" name="otp_uri" value=${this.challenge.configUrl} />

                <div class="pf-c-form__group">
                    <div class="qr-container">
                        <qr-code
                            role="img"
                            aria-label=${msg("QR-Code to setup a time-based one-time password")}
                            format="svg"
                            data="${this.challenge.configUrl}"
                        ></qr-code>
                        <div class="qr-buttons">
                            <button
                                type="button"
                                class="pf-c-button pf-m-secondary pf-m-progress pf-m-in-progress"
                                aria-label=${msg("Copy time-based one-time password configuration")}