vybe/client/auth.js

150 lines
4.2 KiB
JavaScript

import { render, html } from '/uhtml.js';
function rand() {
let str = '';
const lookups =
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'.split('');
while (str.length < 16) {
const n = Math.random() * lookups.length;
str += lookups[Math.floor(n)];
}
return str;
}
async function auth() {
window.session = rand();
window.emit = (event, data, callback) =>
window.socket.emit(event, {
...data,
__session: window.session,
}, callback);
window.signedMessage = await openpgp.sign({
message: new openpgp.CleartextMessage('vybe_auth ' + window.session, ''),
signingKeys: window.keys.priv
});
window.socket.emit('authenticate', {
name: window.name,
id: `${window.id || ''}@${location.host}`,
message: signedMessage,
pubkey: window.keys.armored.publicKey
},
async msg => {
if (!msg.success) {
console.log('authenticate failed:', msg.message);
if (document.getElementById('app'))
location.reload();
document.getElementById('result').innerText = msg.message;
return;
}
localStorage.setItem('keys', JSON.stringify(window.keys.armored));
localStorage.setItem('name', window.name = msg.name);
localStorage.setItem('id', window.id = msg.id);
window.displayname = msg.displayname;
if (window.instancelist) {
if (window.instancelist[0].url !== location.host
|| window.instancelist[0].id !== msg.instance.id) {
console.log('instance url or id changed ??');
return;
}
}
else {
localStorage.setItem('instances', JSON.stringify(window.instancelist = [
{
id: msg.instance.id,
url: location.host
}
]));
}
instancelist[0].socket = window.socket;
instancelist[0].emit = window.emit;
window.instances = Object.fromEntries(instancelist.map(i => [i.url, i]));
document.getElementById('register')?.remove();
const { authRequest } = await import('/app.js');
msg.authrequests.forEach(authRequest);
document.getElementById('profilepublic').checked = msg.public;
});
}
async function submit(event) {
event.preventDefault();
const name = document.getElementById('name').value;
if (!name)
return;
const keys = await openpgp.generateKey({
userIDs: [{ name }]
});
const priv = await openpgp.readKey({ armoredKey: keys.privateKey });
const pub = await openpgp.readKey({ armoredKey: keys.publicKey });
window.keys = { priv, pub, armored: keys };
window.name = name;
if (this.id === 'registerform') {
window.socket.emit('create_user',
{ name, pubkey: keys.publicKey },
(msg) => {
if (!msg.success) {
document.getElementById('result').innerText = msg.message;
return;
}
auth();
}
);
}
else {
await auth();
document.getElementById('result').innerHTML =
`now open your profile on your other device to approve this authentication.
this session's ID is <strong>${pub.getFingerprint().slice(0,8)}</strong>`;
}
}
render(document.body, html`
<div id='register' class='hidden'>
<h1>welcome to vybe</h1>
<h3>a communication network (beta)</h3>
<p>
to get started, you'll need an account. we use public key cryptography
for security, rather than passwords. your keys are stored in your
browser storage only, so do this on a browser you can access again.
</p>
<p>
if you already have an account, enter your username here,
and you can authenticate using your other device.
</p>
<form onsubmit=${submit} id='registerform'>
<label for='name'>username: </label>
<input id='name' type='text' />
<button id='submit' type='submit'>register</button>
<button onclick=${submit}>authenticate</button>
</form>
<p id='result'></p>
</div>
`);
window.onload = async () => {
window.socket = io();
let keys = localStorage.getItem('keys');
if (keys) {
window.name = localStorage.getItem('name');
window.id = localStorage.getItem('id');
window.instancelist = JSON.parse(localStorage.getItem('instances'));
keys = JSON.parse(keys);
window.keys = {
priv: await openpgp.readKey({ armoredKey: keys.privateKey }),
pub: await openpgp.readKey({ armoredKey: keys.publicKey }),
armored: keys
};
await auth();
}
else
document.getElementById('register').classList.remove('hidden');
window.socket.io.on('reconnect', async attempt => {
if (window.keys)
await auth();
});
};