Search code examples
javascriptinputvuejs2one-time-password

How to correctly disable autocomplete for input type password


Hello I'm implementing the OTP functionality modal that can be opened few times, once when you navigating to the app and second on some specific action for example clicking on some action button.

I'm using Vue2 and Buefy if that matters, so the problem is that once I submit the code and open modal again it has previously added code filled in already. I have tried to specify autocomplete as one-time-code or new-password none of them work.

Here is my implementation

Input component

<b-input
      :name="name"
      :value="value"
      :placeholder="placeholder"
      :disabled="disabled"
      :maxlength="maxLength"
      :type="type"
      :autocomplete="autocomplete"
      :inputmode="inputmode"
      @input="(value) => $emit('input', value)"
    >
 </b-input>

JS part

export default {
  name: "Input",
  props: {
    name: String,
    label: String,
    placeholder: String,
    value: String,
    autocomplete: String,
    inputmode: {
      type: String,
      default: "text"
    },
    type: {
      type: String,
      default: "text"
    },
    disabled: {
      type: Boolean,
      default: false
    },
    maxLength: Number
  }
};

OTP modal

<b-modal :active="isOpen" width="200" :can-cancel="canCancel">
    <div>
      <h2>Otp modal</h2>
      <Input
        label="Otp code"
        name="otp"
        v-model="code"
        placeholder="code"
        type="password"
        :maxLength="4"
        autocomplete="one-time-code"
        inputMode="numeric"
      />
      <slot></slot>
      <b-button type="is-primary" @click="close">Submit</b-button>
      <b-button type="is-primary is-light" @click="close">Close</b-button>
    </div>
 </b-modal>

CodeSandbox for clickable demo: https://codesandbox.io/s/buefy-otp-test-onuh1


Solution

  • I think you're confused about what's going on here a little bit– browsers might auto-fill your OTP if you don't set autocomplete correctly, but that's not what's causing your data to stay intact. I can't figure out why (I'm unfamiliar with Buefy), but the state of your OTP modal component is staying intact even after you close the modal; the browser isn't doing anything wrong here.

    One way to address this is by manually clearing the OTP code using a watcher.

    It's not the cleanest solution, and adding too many watchers can quickly make your code difficult to debug, but using only one shouldn't hurt.

    Add this to the end of your OtpModal component's JavaScript:

    ...
      watch: {
        isOpen() {
          this.code = "";
        }
      }
    ...
    

    This will tell Vue to clear code anytime the value of isOpen changes.

    Here's a snippet with the working code, you'll need to open the snippet fullscreen to see it.

    (I removed the input component since it was just a wrapper and nothing else, you shouldn't include it either if it's solely passing data through itself)

    Vue.config.productionTip = false;
    Vue.config.devtools = false;
    
    const OtpModal = {
      name: "otp-modal",
      template: `
      <b-modal :active="isOpen" width="200" :can-cancel="canCancel">
        <div>
          <h2>Otp modal</h2>
          <b-field>
            <b-input
              label="Otp code"
              name="otp"
              v-model="code"
              placeholder="code"
              type="password"
              :maxLength="4"
              autocomplete="one-time-code"
              inputMode="numeric"
            />
          </b-field>
          <slot></slot>
          <b-button type="is-primary" @click="close">Submit</b-button>
          <b-button type="is-primary is-light" @click="close">Close</b-button>
        </div>
      </b-modal>`,
      props: {
        isOpen: {
          type: Boolean,
          default: false
        }
      },
      data() {
        return {
          code: ""
        };
      },
      computed: {
        canCancel() {
          return ["escape", "x", "outside"];
        }
      },
      methods: {
        close() {
          this.$emit("close");
        }
      },
      watch: {
        isOpen() {
          this.code = "";
        }
      },
    };
    
    new Vue({
      name: 'main',
      el: '#app',
      components: {
        OtpModal,
      },
      data() {
        return {
          isModalOpen: false,
        };
      },
      methods: {
        toggleModal() {
          this.isModalOpen = !this.isModalOpen;
        },
        closeModal() {
          this.isModalOpen = false;
        },
      },
    });
    #app {
      font-family: "Avenir", Helvetica, Arial, sans-serif;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
      text-align: center;
      color: #2c3e50;
      margin-top: 60px;
    }
    
    div {
      background-color: #ffffff;
      padding: 2rem;
    }
    <script src="https://unpkg.com/vue@2/dist/vue.js"></script>
    <link rel="stylesheet" href="https://unpkg.com/buefy@0.9.7/dist/buefy.min.css">
    <script src="https://unpkg.com/buefy/dist/buefy.min.js"></script>
    
    <div id="app">
      <b-button @click="toggleModal">Open modal</b-button>
      <otp-modal :is-open="isModalOpen" @close="closeModal">
        <p>Autocomplete isn't the problem afterall!</p>
      </otp-modal>
    </div>

    Also, just to confirm, one-time-code is indeed a valid autocomplete value, and the browser should respect it. off is also valid, and browsers should respect this value today as well (they haven't always been so nice, however).