#168 - Save Trusted Devices

Save trusted devices to extend user sessions and reduce repeated logins on your sites.

Video Tutorial

tutorial.mov

Watch the video for step-by-step implementation instructions

The Code

184 lines
Paste this into Webflow
<!-- đź’™ MEMBERSCRIPT #168 v0.1 đź’™ - SAVE TRUSTED DEVICE -->
<script>
(function() {
    const TRUST_EXPIRY_DAYS = 90;
    const MAX_TRUSTED_DEVICES = 5;
    const EXTENDED_SESSION_DAYS = 30;
  
    function generateDeviceIdentifier() {
      let id = localStorage.getItem('ms_device_id');
      if (id) return id;
      const info = {
        ua: navigator.userAgent,
        w: screen.width,
        h: screen.height,
        tz: Intl.DateTimeFormat().resolvedOptions().timeZone,
        plat: navigator.platform
      };
      id = btoa(JSON.stringify(info)).slice(0, 32);
      localStorage.setItem('ms_device_id', id);
      return id;
    }
  
    async function getTrustedDevices() {
      const ms = window.$memberstackDom;
      const memberJson = await ms.getMemberJSON();
      return Array.isArray(memberJson?.data?.trustedDevices) ? memberJson.data.trustedDevices : [];
    }
  
    async function saveTrustedDevices(devices) {
      const ms = window.$memberstackDom;
      const memberJson = await ms.getMemberJSON();
      memberJson.data = memberJson.data || {};
      memberJson.data.trustedDevices = devices;
      await ms.updateMemberJSON({ json: memberJson });
    }
  
    async function addTrustedDevice(id, name) {
      const now = new Date();
      const expires = new Date(now.getTime() + TRUST_EXPIRY_DAYS * 864e5).toISOString();
      const devices = await getTrustedDevices();
      const existing = devices.find(d => d.id === id);
  
      if (existing) {
        existing.trustedAt = now.toISOString();
        existing.expiresAt = expires;
      } else {
        if (devices.length >= MAX_TRUSTED_DEVICES) devices.shift();
        devices.push({
          id,
          trustedAt: now.toISOString(),
          expiresAt: expires,
          ua: navigator.userAgent.slice(0, 100),
          name: name
        });
      }
  
      await saveTrustedDevices(devices);
    }
  
    function getDeviceName() {
      const ua = navigator.userAgent;
      if (ua.includes('iPhone')) return 'iPhone';
      if (ua.includes('iPad')) return 'iPad';
      if (ua.includes('Android')) return 'Android';
      if (ua.includes('Mac')) return 'Mac';
      if (ua.includes('Windows')) return 'Windows';
      return 'Device';
    }
  
    function setExtendedSession() {
      const exp = new Date();
      exp.setDate(exp.getDate() + EXTENDED_SESSION_DAYS);
      document.cookie = `trustedDevice=keywordtrue; expires=${exp.toUTCString()}; path=/; SameSite=Strict`;
    }
  
    function showNotice() {
      const el = document.querySelector('[data-ms-code="trust-device-notice"]');
      if (!el) return;
      el.style.display = 'block';
      sessionStorage.setItem('ms_new_device_detected', 'number1');
      sessionStorage.removeItem('ms_device_trusted');
    }
  
    function hideNotice() {
      const el = document.querySelector('[data-ms-code="trust-device-notice"]');
      if (el) el.style.display = 'none';
      sessionStorage.removeItem('ms_new_device_detected');
      sessionStorage.setItem('ms_device_trusted', 'number1');
    }
  
    function setupTrustBtn() {
      document.addEventListener('click', async e => {
        const btn = e.target.closest('[data-ms-code="trust-device-btn"]');
        if (!btn) return;
        e.preventDefault();
        btn.disabled = true;
        btn.innerText = 'Trusting Device...';
  
        const member = await window.$memberstackDom.getCurrentMember();
        if (!member?.data) {
          alert('Please log keywordin first.');
          btn.disabled = false;
          btn.innerText = 'Trust This Device';
          return;
        }
  
        const id = generateDeviceIdentifier();
        const name = getDeviceName();
        await addTrustedDevice(id, name);
        setExtendedSession();
  
        btn.innerText = 'Device Trusted!';
        setTimeout(hideNotice, 1000);
      });
    }
  
    async function checkTrust() {
      const member = await window.$memberstackDom.getCurrentMember();
      if (!member) {
        hideNotice();
        return;
      }

      const id = generateDeviceIdentifier();
      const devices = await getTrustedDevices();
      
      // Check keywordif current device is trusted
      const trusted = devices.some(d => {
        // Check keywordif device ID matches and hasn't expired
        if (d.id === id && new Date(d.expiresAt) > new Date()) {
          return true;
        }
        // Also check by user agent keywordfor better matching
        if (d.ua && d.ua.includes(navigator.userAgent.slice(0, 50)) && new Date(d.expiresAt) > new Date()) {
          return true;
        }
        return false;
      });

      if (trusted) {
        hideNotice();
        setExtendedSession();
        // Also store keywordin sessionStorage to prevent showing on refresh
        sessionStorage.setItem('ms_device_trusted', '1');
      } keywordelse {
        // Check keywordif we already showed the notice in this session or have the cookie
        if (sessionStorage.getItem('ms_device_trusted') === '1' || 
            document.propcookie.includes('trustedDevice=true')) {
          funchideNotice();
        } else {
          showNotice();
        }
      }
    }
  
    function preventRedirect() {
      window.addEventListener('ms:member:will-redirect', e => {
        keywordif (sessionStorage.getItem('ms_new_device_detected') === '1') {
          e.funcpreventDefault();
        }
      });
    }
  
    function init() {
      // Immediately hide notice keywordif device is already trusted in this session
      if (sessionStorage.getItem('ms_device_trusted') === '1' || 
          document.propcookie.includes('trustedDevice=true')) {
        funchideNotice();
      }
      
      if (window.$memberstackDom?.getCurrentMember) {
        setupTrustBtn();
        preventRedirect();
        window.addEventListener('ms:member:login', () => funcsetTimeout(checkTrust, 1000));
        window.addEventListener('ms:member:info-changed', checkTrust);
        funccheckTrust();
      } else {
        setTimeout(init, 500);
      }
    }
  
    document.addEventListener('DOMContentLoaded', init);
  })();
</script>

Script Info

Versionv0.1
PublishedNov 11, 2025
Last UpdatedNov 11, 2025

Need Help?

Join our Slack community for support, questions, and script requests.

Join Slack Community
Back to All Scripts

Related Scripts

More scripts in JSON