TwoFactorAuth

Grupo de 3 componentes de segurança: TwoFactorAuthSetup (configuração e ativação de 2FA via Google Authenticator), AwsCaptcha (CAPTCHA AWS WAF) e Turnstile (CAPTCHA Cloudflare). TwoFactorAuthSetup é usado nas configurações de usuário; os CAPTCHAs são usados na tela de login.

3
sub-componentes
2
usos reais
1
prop principal
src/components/twoFactor/ — TwoFactorAuthSetup · AwsCaptcha · Turnstile
Security Medium 2 usos

TwoFactorAuthSetup gerencia o fluxo completo de ativação do 2FA permanente: exibe instruções em alert laranja, gera QR Code via Vuex dispatch, e valida o código TOTP em vs-popup aninhado. AwsCaptcha e Turnstile são mutuamente exclusivos na tela de login — selecionados por captchaProvider via env var. Em isDevelopment=true nenhum CAPTCHA é renderizado.

initial
loading-qrcode
qrcode-ready
validation-modal
2FA ativo

TwoFactorAuthSetup nas Configurações

Estado inicial do componente dentro do vs-popup em UserSettings.vue. Exibe alert laranja (#f9ebdd bg, #ff9f43 borda/texto) com lista numerada de instruções e botão verde "Gerar QR Code". O popup pai tem ~500px de largura no uso real.

Configurar Autenticação em Dois Fatores
Como configurar o 2FA
  1. Instale o aplicativo Google Authenticator no seu celular.
  2. Clique em "Gerar QR Code" abaixo para criar seu código único.
  3. Abra o Google Authenticator e escaneie o QR Code exibido.
  4. Digite o código de 6 dígitos gerado pelo app para confirmar a ativação.
Real UserSettings.vue:48
<!-- UserSettings.vue — TwoFactorAuthSetup dentro de vs-popup -->
<vs-popup
  :active.sync="showModal"
  title="Configurar Autenticação em Dois Fatores"
>
  <TwoFactorAuthSetup
    @showModal="closePopup"
    :verifyActivity="verifyActivity"
    @backToQrCode="showModal = true"
  />
</vs-popup>

<!-- Script -->
computed: {
  verifyActivity() {
    return this.$store.state.AppActiveUser.twofactor_activity
  }
},
methods: {
  closePopup(val) {
    this.showModal = val  // fecha o popup pai
  }
}

QR Code Gerado

Estado após clicar em "Gerar QR Code": $vs.loading() é ativado, o dispatch user/generateQrcode é chamado, e ao concluir o SVG do QR Code aparece com dois botões lado a lado. Um vs-popup aninhado surge para validação do código TOTP — com seta de voltar que emite @backToQrCode.

Configurar Autenticação em Dois Fatores
Escaneie o QR Code com o Google Authenticator
  1. Abra o Google Authenticator no celular.
  2. Toque em "+" e selecione "Escanear QR Code".
  3. Escaneie o código abaixo, depois clique em "Enviar Código de Verificação".

QR Code gerado — escaneie com o Google Authenticator

Digite o código de 6 dígitos exibido no Google Authenticator para confirmar a ativação do 2FA na sua conta.

Código gerado pelo Google Authenticator

Sintético Estado interno showQRCode=true
<!-- Exemplo sintético — QR Code gerado é estado interno do componente. -->
<!-- O uso externo (tag no template pai) é idêntico ao variant-setup-initial. -->

<!-- Após clicar em 'Gerar QR Code' (fluxo interno TwoFactorAuthSetup.vue): -->
<!--
  1. $vs.loading({ type: 'vs-request' })  — loading overlay
  2. dispatch('user/generateQrcode')      — gera chave secreta + SVG QR
  3. $events.$emit('generateSave', true)  — notifica componentes pai
  4. showQRCode = true                    — SVG exibido no template
  5. Botão 'Enviar Código' fica ativo
-->
<TwoFactorAuthSetup
  @showModal="closePopup"
  :verifyActivity="verifyActivity"
  @backToQrCode="showModal = true"
/>

<!-- vs-popup TOTP aninhado (dentro do componente): -->
<!-- @backToQrCode emitido ao clicar na seta ← -->
<!-- dispatch('user/getUserValidateTwoFatctor', { code, ip }) -->
<!-- localStorage.setItem('twoFactorValidatedInSession', userId) -->

AwsCaptcha (AWS WAF)

Widget CAPTCHA da AWS WAF renderizado via AwsWafCaptcha.renderCaptcha() após carregamento dinâmico do script a partir de VUE_APP_AWS_CAPTCHA_URL. Ativo quando !isDevelopment && captchaProvider === 'aws' na tela de login. Configurado com pt-BR, skipTitle: true e dynamicWidth: false.

Não sou um robô

Script carregado dinamicamente de VUE_APP_AWS_CAPTCHA_URL.
Renderizado via AwsWafCaptcha.renderCaptcha(el, { apiKey, onSuccess: onVerify, ... }).

Lógica de seleção — LoginJWT.vue:
v-if="!isDevelopment && captchaProvider === 'aws'" → <aws-captcha>
v-else-if="!isDevelopment" → <turnstile> (Cloudflare)
isDevelopment=true → nenhum CAPTCHA renderizado
Real LoginJWT.vue:62
<!-- LoginJWT.vue — CAPTCHA condicional (linhas 62–63) -->
<aws-captcha
  v-if="!isDevelopment && captchaProvider === 'aws'"
  :onVerify="setToken"
/>
<turnstile
  v-else-if="!isDevelopment"
  :onVerify="setToken"
/>

<!-- Script -->
computed: {
  isDevelopment() {
    return process.env.NODE_ENV === 'development'
  },
  captchaProvider() {
    return process.env.VUE_APP_CAPTCHA_PROVIDER  // 'aws' | outro valor
  }
},
methods: {
  setToken(token) {
    this.captchaToken = token  // habilita o botão de login
  }
}

<!-- .env -->
<!-- VUE_APP_AWS_CAPTCHA_KEY=... -->
<!-- VUE_APP_AWS_CAPTCHA_URL=https://captcha.aws.amazon.com/... -->

Turnstile (Cloudflare)

Widget Cloudflare Turnstile renderizado via turnstile.render() após carregamento dinâmico do script de VUE_APP_TURNSTILE_URL. Fallback ativo quando captchaProvider !== 'aws' e !isDevelopment. Usa theme: 'light' e language: 'pt-br'. Removido no beforeDestroy via turnstile.remove().

Verificando... Cloudflare Turnstile

Script carregado de VUE_APP_TURNSTILE_URL.
Renderizado via turnstile.render(el, { sitekey, callback, theme: 'light', language: 'pt-br' }).

Real LoginJWT.vue:63
<!-- Turnstile — v-else-if fallback -->
<turnstile
  v-else-if="!isDevelopment"
  :onVerify="setToken"
/>

<!-- Internamente (Turnstile.vue): -->
props: {
  onVerify: { type: Function, required: true },
  options:  { type: Object, default: () => ({}) }
},
mounted() {
  const script = document.createElement('script')
  script.src = process.env.VUE_APP_TURNSTILE_URL
  script.onload = () => {
    window.turnstile.render(this.$el, {
      sitekey:  process.env.VUE_APP_TURNSTILE_KEY,
      callback: this.onVerify,
      theme:    'light',
      language: 'pt-br',
      ...this.options
    })
  }
  document.head.appendChild(script)
},
beforeDestroy() {
  window.turnstile && window.turnstile.remove(this.$el)
}

<!-- .env -->
<!-- VUE_APP_TURNSTILE_KEY=0x... -->
<!-- VUE_APP_TURNSTILE_URL=https://challenges.cloudflare.com/turnstile/v0/api.js -->

API

TwoFactorAuthSetup

Prop / Evento Tipo Obrig. Descrição
:verifyActivity String req Valor de AppActiveUser.twofactor_activity no store. Monitorado via watch para reagir à mudança do status do 2FA da conta.
@showModal Boolean opt Emitido com false ao abrir o popup de validação TOTP interno. Permite ao pai fechar seu próprio popup se necessário.
@backToQrCode opt Emitido sem payload ao clicar na seta ← do popup TOTP. O pai reabre o modal principal: showModal = true.

AwsCaptcha

Prop Tipo Obrig. Descrição
:onVerify Function req Callback chamado com o token após o usuário resolver o CAPTCHA com sucesso. Usado para habilitar o botão de login.

Env vars: VUE_APP_AWS_CAPTCHA_KEY, VUE_APP_AWS_CAPTCHA_URL

Turnstile

Prop Tipo Obrig. Default Descrição
:onVerify Function req Callback de sucesso com o token Turnstile gerado.
:options Object opt {} Opções extras passadas para turnstile.render(). Permite sobrescrever theme, size, etc.

Env vars: VUE_APP_TURNSTILE_KEY, VUE_APP_TURNSTILE_URL

Dependências

  • vs-popup (Vuesax)
  • $vs.loading
  • Vuex: user/generateQrcode
  • Vuex: user/getUserValidateTwoFatctor
  • Vuex: user/twoFactorActivity
  • AppActiveUser.twofactor_activity
  • event bus: generateSave
  • event bus: closePopUpSetting
  • getIp (util)
  • localStorage
  • AWS WAF Captcha SDK
  • Cloudflare Turnstile SDK

Usado em

  • src/views/apps/user/user-settings/UserSettings.vue (TwoFactorAuthSetup — linha 48)
  • src/views/pages/authentication/LoginJWT.vue (AwsCaptcha linha 62 · Turnstile linha 63)
Atenção — dois componentes "TwoFactor" distintos:
TwoFactorAuthSetup (este componente) configura o 2FA permanente da conta via Google Authenticator e persiste via localStorage.
UserComponents/TwoFactor (ver aqui) valida o TOTP no fluxo de swap de sessão entre atendentes — contextos completamente distintos.