Skip to content

Instantly share code, notes, and snippets.

@Falci
Created April 3, 2021 17:24
Show Gist options
  • Save Falci/8e12be1b9538c4521a3d312a02e4682d to your computer and use it in GitHub Desktop.
Save Falci/8e12be1b9538c4521a3d312a02e4682d to your computer and use it in GitHub Desktop.
const fs = require('fs');
const crypto = require('crypto');
const decB64 = b64 => Buffer.from(b64, 'base64').toString('ascii');
const encB64 = ascii => Buffer.from(ascii).toString('base64');
const saveKeyToFile = (filename, key) =>
fs.writeFileSync(filename, key.export({ type: 'pkcs8', format: 'pem' }));
const loadKeyFromFile = filename => loadRawKey(fs.readFileSync(filename));
const loadRawKey = raw => crypto.createPrivateKey(raw);
const keyToPublicKey = key => crypto.createPublicKey(key).export({ type: 'spki', format: 'pem' });
const fingerprint = key => crypto.createHash('sha256').update(keyToPublicKey(key)).digest('hex');
const generateKey = () =>
crypto.generateKeyPair('rsa', {
modulusLength: 4046,
privateKeyEncoding: { type: 'pkcs8', format: 'der' },
}).privateKey;
const generateUrl = ({ id, state, callbackUrl }, key, deviceId) => {
const url = new URL(decB64(callbackUrl)); // b64 => str => url
const challenge = Buffer.from(state, 'base64'); // b64 => Buffer
const signed = crypto.sign('sha512', challenge, {
key,
padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
saltLength: 64,
});
const publicKey = keyToPublicKey(key);
const data = {
signed: encB64(signed.toString('base64')), // Buffer => b64 => b64 (https://github.com/namebasehq/handshake-id-manager/issues/6)
domain: id, // b64
deviceId: encB64(deviceId), // str => b64
publicKey: encB64(publicKey), // str => b64
};
url.hash = encB64(JSON.stringify(data)); // JSON => str => b64
return url;
};
module.exports = {
decB64,
encB64,
generateKey,
generateUrl,
keyToPublicKey,
loadKeyFromFile,
loadRawKey,
fingerprint,
saveKeyToFile,
};
// The following is the code used to process the login request:
const { loadKeyFromFile, generateUrl } = require('./login-with-hns');
// 1. Load the private key:
const key = loadKeyFromFile('my-private.key');
// 2. Use the same deviceId:
const deviceId = 'my-device';
// 3. Generate the url:
// id, state and callbackUrl should be in the same format as received in the URL hash:
const url = generateUrl({ id, state, callbackUrl }, key, deviceId);
// 4. Redirect
res.redirect(url.toString());
// This is code to setup your name to use "Login with Handshake"
const { generateKey, saveKeyToFile, fingerprint } = require('./login-with-hns');
// 0. For this example, we will login with the TLD: "example"
const name = 'example';
// 1. The private key
const key = generateKey();
// 2. Make a backup of your private key:
saveKeyToFile('my-private.key', key);
// 3. Create a custom deviceId:
const deviceId = 'my-device';
// 4. Generate the Fingerprint
const fp = fingerprint(key);
// 5. Create a TXT record to store the fingerprint:
// my-device._auth.example. IN TXT "v=0;fingerprint=1790b098060455d8e2f110a8847a063221e3ef5060f0e103be1c42788d80983f"
console.log(`${deviceId}._auth.${name}. IN TXT "v=0;fingerprint=${fp}"`);
// 6. Create a TXT record to tell the OIDC where to login
// _idmanager.example. IN TXT v=0;url=https://example/
console.log(`_idmanager.${name}. IN TXT v=0;url=https://${name}/`);
// 6.1. The OIDC will make a call to you like:
// https://example/#/login?state=YXBGdjdQcUpUdmQ3NU00YlVOaVVUWnpjcEstNWduMlNQMU9VSWR1eDg3UQ==&id=ZXhhbXBsZQ==&callbackUrl=aHR0cHM6Ly9vaWRjLm5hbWViYXNlLmlvL2xvZ2luL3VoeUd5LVh3Wk1KUlQxVS1jdGtNRS9jaGFsbGVuZ2U=
// Note this URL uses hash (#)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment