GoogleChromeLabs / web-push-codelab

Other
556 stars 291 forks source link

The application server key generator companion site 404s #54

Closed kevineger closed 7 years ago

kevineger commented 7 years ago

The Get Application Server Keys step of the code lab references https://web-push-codelab.appspot.com/, but that link 404s.

I found https://web-push-codelab.glitch.me/ which appears to work.

gauntface commented 7 years ago

Yes I've updated the codelab it's just not updated on web fundamentals.

kevineger commented 7 years ago

@gauntface , I wasn't sure where else to submit the issue. The web fundamentals code lab links this repo as the place to do so.

gauntface commented 7 years ago

@kevineger I'm working with folks on WebFundamentals team. My assumption was they would build from the course of truth automatically. Looks like it requires a manual kick.

edusantana commented 7 years ago

@gauntface can you provide here the information about where or how to create these keys?

If we can do it with a linux command it's even better.

edusantana commented 7 years ago

For example, does the command ssh-keygen -b 1024 -t rsa -f my_rsa_key.txt works? What kind of key https://web-push-codelab.appspot.com was generating?

gauntface commented 6 years ago

@edusantana you can use any library that outputs VAPID keys. The companion site is doing this:

/* eslint-env browser, es6 */

function base64UrlToUint8Array(base64UrlData) {
  const padding = '='.repeat((4 - base64UrlData.length % 4) % 4);
  const base64 = (base64UrlData + padding)
    .replace(/\-/g, '+')
    .replace(/_/g, '/');

  const rawData = window.atob(base64);
  const buffer = new Uint8Array(rawData.length);

  for (let i = 0; i < rawData.length; ++i) {
    buffer[i] = rawData.charCodeAt(i);
  }
  return buffer;
}

function uint8ArrayToBase64Url(uint8Array, start, end) {
  start = start || 0;
  end = end || uint8Array.byteLength;

  const base64 = window.btoa(
    String.fromCharCode.apply(null, uint8Array.subarray(start, end)));
  return base64
    .replace(/\=/g, '') // eslint-disable-line no-useless-escape
    .replace(/\+/g, '-')
    .replace(/\//g, '_');
}

function cryptoKeyToUrlBase64(publicKey, privateKey) {
  const promises = [];
  promises.push(
    crypto.subtle.exportKey('jwk', publicKey)
    .then((jwk) => {
      const x = base64UrlToUint8Array(jwk.x);
      const y = base64UrlToUint8Array(jwk.y);

      const publicKey = new Uint8Array(65);
      publicKey.set([0x04], 0);
      publicKey.set(x, 1);
      publicKey.set(y, 33);

      return publicKey;
    })
  );

  promises.push(
    crypto.subtle
      .exportKey('jwk', privateKey)
    .then((jwk) => {
      return base64UrlToUint8Array(jwk.d);
    })
  );

  return Promise.all(promises)
  .then((exportedKeys) => {
    return {
      public: uint8ArrayToBase64Url(exportedKeys[0]),
      private: uint8ArrayToBase64Url(exportedKeys[1]),
    };
  });
}

function generateNewKeys() {
  return crypto.subtle.generateKey({name: 'ECDH', namedCurve: 'P-256'},
    true, ['deriveBits'])
  .then((keys) => {
    return cryptoKeyToUrlBase64(keys.publicKey, keys.privateKey);
  });
}

function clearKeys() {
  window.localStorage.removeItem('server-keys');
}

function storeKeys(keys) {
  window.localStorage.setItem('server-keys', JSON.stringify(keys));
}

function getStoredKeys() {
  const storage = window.localStorage.getItem('server-keys');
  if (storage) {
    return JSON.parse(storage);
  }

  return null;
}

function displayKeys(keys) {
  const publicElement = document.querySelector('.js-public-key');
  const privateElement = document.querySelector('.js-private-key');
  const refreshBtn = document.querySelector('.js-refresh-keys');

  publicElement.textContent = keys.public;
  privateElement.textContent = keys.private;

  refreshBtn.disabled = false;
}

function updateKeys() {
  let storedKeys = getStoredKeys();
  let promiseChain = Promise.resolve(storedKeys);
  if (!storedKeys) {
    promiseChain = generateNewKeys()
    .then((newKeys) => {
      storeKeys(newKeys);
      return newKeys;
    });
  }

  return promiseChain.then((keys) => {
    displayKeys(keys);
  });
}

function initialiseKeys() {
  const refreshBtn = document.querySelector('.js-refresh-keys');
  refreshBtn.addEventListener('click', function() {
    refreshBtn.disabled = true;

    clearKeys();

    updateKeys();
  });

  updateKeys();
}

window.addEventListener('load', () => {
  initialiseKeys();
});

You can also use tools like web-push npm module: https://github.com/web-push-libs/web-push