HEX
Server: Microsoft-IIS/10.0
System: Windows NT ITPWINWEBSVR22 10.0 build 20348 (Windows Server 2022) AMD64
User: www.conferencesearch.co.uk (0)
PHP: 8.3.30
Disabled: NONE
Upload Files
File: D:/web/matomo.vdk/plugins/TwoFactorAuth/vue/src/SetupTwoFactorAuth/SetupTwoFactorAuth.vue
<!--
  Matomo - free/libre analytics platform

  @link    https://matomo.org
  @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
-->

<template>
  <ContentBlock
    :content-title="standalone
      ? translate('TwoFactorAuth_RequiredToSetUpTwoFactorAuthentication')
      : translate('TwoFactorAuth_SetUpTwoFactorAuthentication')"
  >
    <div class="setupTwoFactorAuthentication" ref="root">
      <div class="alert alert-warning" v-if="isAlreadyUsing2fa">
        {{ translate('TwoFactorAuth_WarningChangingConfiguredDevice') }}
      </div>
      <p>
        {{ translate('TwoFactorAuth_SetupIntroFollowSteps') }}
      </p>
      <h2>
        {{ translate('TwoFactorAuth_StepX', 1) }} - {{ translate('TwoFactorAuth_RecoveryCodes') }}
      </h2>

      <ShowRecoveryCodes
        :codes="codes"
        @downloaded="this.hasDownloadedRecoveryCode = true"
      />

      <div
        class="alert alert-info backupRecoveryCodesAlert"
        v-show="step === 1"
      >
        {{ translate('TwoFactorAuth_SetupBackupRecoveryCodes') }}
      </div>
      <p>
        <button
          class="btn goToStep2"
          v-show="step === 1"
          @click="nextStep()"
          :disabled="!hasDownloadedRecoveryCode"
        >{{ translate('General_Next') }}</button>
      </p>
      <a
        name="twoFactorStep2"
        id="twoFactorStep2"
        style="opacity: 0"
      />
      <div v-show="step >= 2">
        <h2>
          {{ translate('TwoFactorAuth_StepX', 2) }} -
          {{ translate('TwoFactorAuth_SetupAuthenticatorOnDevice') }}
        </h2>
        <InstallOTPApp />
        <p v-html="$sanitize(setupAuthenticatorOnDeviceStep2ShowCodes)"></p>
        <p>
          <br />
          <button
            class="btn showOtpCodes"
            v-show="step >= 2"
            @click="showQrCodeModal()"
          >{{ translate('TwoFactorAuth_ShowCodes') }}</button>
        </p>
      </div>
      <a
        name="twoFactorStep3"
        id="twoFactorStep3"
        style="opacity: 0"
      />
      <div v-show="step >= 3">
        <h2>
          {{ translate('TwoFactorAuth_StepX', 3) }} - {{ translate('TwoFactorAuth_ConfirmSetup') }}
        </h2>
        <p>{{ translate('TwoFactorAuth_VerifyAuthCodeIntro') }}</p>
        <div class="message_container" v-if="accessErrorString">
          <div>
            <Notification
              :noclear="true"
              context="error"
            >
              <strong>
                {{ translate('General_Error') }}
              </strong>: <span v-html="$sanitize(accessErrorString)"/><br />
            </Notification>
          </div>
        </div>
        <form
          method="post"
          class="setupConfirmAuthCodeForm"
          autocorrect="off"
          autocapitalize="none"
          autocomplete="off"
          :action="linkTo({'module': 'TwoFactorAuth', 'action': submitAction})"
        >
          <div>
            <Field
              uicontrol="text"
              name="authCode"
              :title="translate('TwoFactorAuth_AuthenticationCode')"
              v-model="authCode"
              :maxlength="6"
              :placeholder="'123456'"
              autocomplete="one-time-code"
              :inline-help="translate('TwoFactorAuth_VerifyAuthCodeHelp')"
            >
            </Field>
          </div>
          <input
            type="hidden"
            name="authCodeNonce"
            :value="authCodeNonce"
          />
          <input
            type="submit"
            class="btn confirmAuthCode"
            :disabled="authCode.length !== 6"
            :value="translate('General_Confirm')"
          />
        </form>
      </div>

      <MatomoDialog
        v-model="qrCodeDialogVisible"
        @validation="closeQrCodeModal(); nextStep()"
        :options="{ focusSelector: '.modal-action.btn'}"
      >
        <div
          class="ui-confirm two-fa-qr-code-dialog"
        >
          <h2>{{ translate('TwoFactorAuth_Your2FaAuthSecret') }}</h2>
          <div class="row">
            <div class="col l8 offset-l2 m10 offset-m1 s12 center-align">
              <p>{{ translate('TwoFactorAuth_ShowCodeModalInstructions1') }}</p>
              <p>
                <span
                  id="qrcode"
                  ref="qrcode"
                  title
                />
              </p>
              <p>{{ translate('TwoFactorAuth_ShowCodeModalInstructions2') }}</p>

              <div class="text-code">
                <pre v-copy-to-clipboard="{}">{{ newSecret}}</pre>
              </div>

              <p v-html="$sanitize(showCodeModalInstructions3)"></p>
            </div>
          </div>

          <div class="row">
            <div class="col l8 offset-l2 m10 offset-m1 s12">
              <h3>{{ translate('TwoFactorAuth_DontHaveOTPApp') }}</h3>
              <InstallOTPApp />
            </div>
          </div>

          <input role="validation" type="button" :value="translate('General_Continue')"/>
          <input role="no" type="button" :value="translate('General_Cancel')"/>
        </div>
      </MatomoDialog>
    </div>
  </ContentBlock>
</template>

<script lang="ts">
import { defineComponent } from 'vue';
import {
  translate,
  Matomo,
  Notification,
  MatomoUrl,
  ContentBlock,
  MatomoDialog,
  CopyToClipboard,
} from 'CoreHome';
import { Field } from 'CorePluginsAdmin';
import '../types';
import ShowRecoveryCodes from '../ShowRecoveryCodes/ShowRecoveryCodes.vue';
import InstallOTPApp from './InstallOTPApp.vue';

interface SetupTwoFactorAuthState {
  step: number;
  hasDownloadedRecoveryCode: boolean;
  authCode: string;
  qrCodeDialogVisible: boolean;
}

const { QRCode, $ } = window;

export default defineComponent({
  props: {
    isAlreadyUsing2fa: Boolean,
    accessErrorString: String,
    submitAction: {
      type: String,
      required: true,
    },
    authCodeNonce: {
      type: String,
      required: true,
    },
    newSecret: {
      type: String,
      required: true,
    },
    codes: Array,
    twoFaBarCodeSetupUrl: {
      type: String,
      required: true,
    },
    standalone: Boolean,
  },
  components: {
    InstallOTPApp,
    MatomoDialog,
    ShowRecoveryCodes,
    Notification,
    Field,
    ContentBlock,
  },
  directives: {
    CopyToClipboard,
  },
  data(): SetupTwoFactorAuthState {
    return {
      step: 1,
      hasDownloadedRecoveryCode: false,
      authCode: '',
      qrCodeDialogVisible: false,
    };
  },
  mounted() {
    setTimeout(() => {
      const qrcode = this.$refs.qrcode as HTMLElement;

      // eslint-disable-next-line no-new
      new QRCode(qrcode, {
        text: this.twoFaBarCodeSetupUrl,
        width: 200,
        height: 200,
      });

      $(qrcode).attr('title', ''); // do not show secret on hover

      if (this.accessErrorString) {
        // user entered something wrong
        this.step = 3;
        this.scrollToEnd();
      }
    });
  },
  methods: {
    scrollToEnd() {
      setTimeout(() => {
        let id = '';
        if (this.step === 2) {
          id = '#twoFactorStep2';
        } else if (this.step === 3) {
          id = '#twoFactorStep3';
        }

        if (id) {
          Matomo.helper.lazyScrollTo(id, 50, true);
        }
      }, 50);
    },
    showQrCodeModal() {
      this.qrCodeDialogVisible = true;
    },
    closeQrCodeModal() {
      this.qrCodeDialogVisible = false;
    },
    nextStep() {
      this.step += 1;
      if (this.step > 3) {
        this.step = 3;
      }
      this.scrollToEnd();
    },
    linkTo(params: QueryParameters) {
      return `?${MatomoUrl.stringify({
        ...MatomoUrl.urlParsed.value,
        ...params,
      })}`;
    },
  },
  computed: {
    setupAuthenticatorOnDeviceStep2ShowCodes() {
      return translate(
        'TwoFactorAuth_SetupAuthenticatorOnDeviceStep2ShowCodes',
        translate('TwoFactorAuth_ShowCodes'),
      );
    },
    showCodeModalInstructions3() {
      return translate(
        'TwoFactorAuth_ShowCodeModalInstructions3',
        translate('General_Continue'),
      );
    },
  },
});
</script>