Enabling 4-digit pincode autofill using cookies

Tags: ,



I have a simple form where one needs to enter a 4-digit PIN code. However, I would also like to autofill that PIN code when the user comes back to the website again, using JS cookies.

JS:

function loginCheck() {
    var pinCode = document.getElementById("pinCode").value;
        if (pinCode.match(/^[0-9]+$/) != null) {
            if (pinCode.length == 4) {
                function setCookie(cname, cvalue) {
                    document.cookie = cname + "=" + cvalue + ";"
                }
                function getCookie(cname) {
                    var name = cname + "=";
                    var ca = document.cookie.split(';');
                    for(var i = 0; i < ca.length; i++) {
                        var c = ca[i];
                        while (c.charAt(0) == ' ') {
                            c = c.substring(1);
                        }
                        if (c.indexOf(name) == 0) {
                            return c.substring(name.length, c.length);
                        }
                    }
                    return "";
                }
                function checkCookie() {
                    var pinCode = document.getElementById("pinCode").value;
                    var userPIN = getCookie("userPIN");
                    if (userPIN != "") {
                        pinCode.value = userPIN;
                    } else {
                        setCookie("username", userPIN);
                    }
                }
                checkCookie();
            } else {
                document.getElementById("rightorwrong").innerHTML = "Not 4 digits!";
            }
        } else {
            document.getElementById("rightorwrong").innerHTML = "Not a number!";
        }
}

HTML:

<div id = "validation">
            <form id = "validationForm" target = "frame">
                <fieldset>
                    <label for = "pass">Password:</label><br  />
                    <input type = "text" id = "pass" name = "pass"  /><br  />
                    <label for = "pinCode">4-digit PIN:</label><br  />
                    <input type = "text" id = "pinCode" name = "pinCode"  /><br  />
                    <input type = "submit" value="Log In" onclick = "loginCheck()"  />
                </fieldset>
            </form>
        </div>
        <p id = "rightorwrong"></p>

I’m aware of a few things that are wrong about this code.

  • in the checkCookie() function, if the user has a cookie stored, then I’m not entirely sure how to retrieve the PIN they first inputted.
  • Defining functions inside functions, and calling them by simply doing checkCookie(); and nothing else, is generally bad practice.
  • When I run checkCookie(); it only does the first part of the if statement and not the second part. I’m not sure why and I couldn’t figure this out.
  • The code in general may have some errors. I modified a cookies script from here but it doesn’t seem to work.

I’m new to the idea of cookies, and am still trying to learn them. A step-by-step explanation would be more helpful.

Help would be greatly appreciated, TIA.

Answer

For cookies I use my “simpleCookie” object with the set / getVal methods to read or save a cookie.

ex:

simpleCookie.setVal( 'my cookie', 'ab/kjf;c', 3 )

let valueX = simpleCookie.getVal('my cookie'))  // return 'ab/kjf;c'
 
simpleCookie.setVal( 'my cookie', '', -1)  remove the cookie

this object is achieved via an IIEF function, and I strongly advise you to use the mozilla documentation

Since automatic form validation exists, I no longer use a text box to indicate an input error, but I have diverted its “normal” use a bit because I find it very restrictive, as you will see in my code.

When at the base of your question, you just have to find a match between the name entered and a possible cookie with the same name, then save this cookie if the form is valid.

Oh, and I also put some css to simplify writing html (no more need for <br>)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>login form</title>
  <style>
    fieldset { margin: 1em; width: 15em; }
    fieldset * { display: block; float: left; clear: both; margin-top: 0.2em; }
    fieldset label { margin-top: 0.7em; }
    fieldset button { margin-top: 2em; }
    fieldset button:last-of-type { clear: none; float: right; }
   </style>
</head>
<body>
  <form id="login-form" action="">
    <fieldset>
      <label>Name:</label> 
      <input type="text" name="name" autocomplete="off"  pattern="[A-Za-z0-9]{1,20}"> 
      <label>Password:</label> 
      <input type="password" name="pass" pattern="[A-Za-z0-9]{1,20}"> 
      <label>4-digit PIN:</label> 
      <input type="text" name="pinCode" autocomplete="off" pattern="[0-9]{4}"> 
      <button type="reset">clear</button>
      <button type="submit">Log In</button>
    </fieldset>
  </form>

  <script src="simpleCoolie.js"></script> <!-- the cookie object (IIFE) -->
  <script src="login_form.js"></script>
</body>
</html>

simpleCoolie.js :

// the cookie object (IIFE)
const simpleCookie = (function()
  {
  const OneDay_ms = 24 *60 *60 *1000  // one Day in milliseconds 
  return { 
    setVal:(cName, cValue='', exDays=10)=>  // 10 days is default cookie recovery, 
      {                                    // negative value remove the cookie
      cName  = encodeURIComponent(cName.trim())
      cValue = encodeURIComponent(cValue.trim())
      if (cName)
        {
        let dte = new Date()
        dte.setTime(dte.getTime() + (exDays *OneDay_ms))          
        document.cookie = `${cName}=${cValue};expires=${dte.toUTCString()};SameSite=Strict;path=/`
      } }
  , getVal:cName=>
      {
      cName = encodeURIComponent(cName.trim())
      let xCookie = document.cookie.split('; ').find(x=>x.startsWith(`${cName}=`))
      return xCookie ? decodeURIComponent(xCookie.split('=')[1]) : ''
    } }
  })()

login_form.js :

const formLogin        = document.getElementById('login-form')
  ,   msgErrorDuration = 5000
  ,   checkInputs =
        [...formLogin.querySelectorAll('input[pattern]')]
          .map(el=>
            {
            let pattern = el.pattern
            el.removeAttribute('pattern')
            return { name:el.name, pattern } 
            });

// generic set checking for report validyty
const getCheckingValidity=(formElement, patternValue)=>
  {
  formElement.pattern  = patternValue
  formElement.required = true
  return formElement.reportValidity()  
  }
// generic checking remove after delay 
const unCheckElement=(formElement,isOK)=>
  {
  formElement.removeAttribute('pattern')
  formElement.required = false

  if(!isOK)
    {
    formElement.setCustomValidity('')
    if(document.activeElement === formElement )  // bugg fix: Firefox doesnt remove error message after delay 
      {                                         // (other browser do)
      formElement.blur();                      // double flip focus
      formElement.focus();                    // --> will remove message bubble
      }
    }
  }

// client-side form validation mecanism to get error message for each input
formLogin.name.oninvalid=_=>
  {
  formLogin.name.setCustomValidity('Please enter a name')
  setTimeout(unCheckElement, msgErrorDuration, formLogin.name, false)
  }
formLogin.pass.oninvalid=_=>
  {
  formLogin.pass.setCustomValidity("can't do anything without password !")
  setTimeout(unCheckElement, msgErrorDuration, formLogin.pass, false)
  }
formLogin.pinCode.oninvalid=_=>
  {
  if (formLogin.pinCode.value==='')
    { formLogin.pinCode.setCustomValidity("PIN code can't be empty !") }
  else
    { formLogin.pinCode.setCustomValidity('PIN code must be 4 digits') }
  setTimeout(unCheckElement, msgErrorDuration, formLogin.pinCode, false)
  }

formLogin.onsubmit=e=>
  {
  let validForm = true 

  for (let Elm of checkInputs)  
    {
    validForm = validForm && getCheckingValidity(formLogin[Elm.name], Elm.pattern )
    if (validForm)
      { unCheckElement(formLogin[Elm.name], true) }
    else break 
    } 
  if (validForm)
    { simpleCookie.setVal( formLogin.name.value, formLogin.pinCode.value ) }
  else
    { e.preventDefault() } // disable form submiting
  }

formLogin.name.oninput=()=>  // check for cookie pin code on name
  {
  formLogin.pinCode.value = simpleCookie.getVal(formLogin.name.value)
  }

In 2009, session/localStorage arrived, which can replace cookies, especially for this kind of use.

To not have to redo all the previous logic, I created here a module called pseudoCookie which actually uses localStorage

here is the complete code to test with it:

// the pseudo cookie object (IIFE)
const pseudoCookie = (function()  // use localStorage 
  {
  return {
    setVal:(cName, cValue='', exDays=10)=> // negative value remove the value in localstorage
      {                          // the values are kept until your browser or your system crashes
      cName  = encodeURIComponent(cName.trim())
      cValue = encodeURIComponent(cValue.trim())
      if (cName) {
        if   (exDays < 0)  localStorage.removeItem(cName)
        else               localStorage.setItem(cName, cValue)
      } }
  , getVal:cName=>
      {
      cName = encodeURIComponent(cName.trim())
      let xCookie = localStorage.getItem(cName)
      return xCookie ? decodeURIComponent(xCookie) : ''
    } }
  })()

and the part to change in JS:

formLogin.onsubmit=e=>
  {
  let validForm = true 

  for (let Elm of checkInputs)  
    {
    validForm = validForm && getCheckingValidity(formLogin[Elm.name], Elm.pattern )
    if (validForm)
      { unCheckElement(formLogin[Elm.name], true) }
    else break 
    } 
  if (validForm)
    { pseudoCookie.setVal( formLogin.name.value, formLogin.pinCode.value ) }
  else
    { e.preventDefault() } // disable form submiting
  }

formLogin.name.oninput=()=>  // check for cookie pin code on name
  {
  formLogin.pinCode.value = pseudoCookie.getVal(formLogin.name.value)
  }


Source: stackoverflow