Authentication flow changes
parent
989ea77e84
commit
57c89a6b31
13
DOCS.md
13
DOCS.md
|
@ -13,6 +13,17 @@ if a message fails, you get a response with the following format:
|
|||
}
|
||||
```
|
||||
|
||||
## authenticate
|
||||
|
||||
you must generate a random salt, sign a message `vybe_auth [salt]`, and then send
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "username",
|
||||
"message": "that message you signed"
|
||||
}
|
||||
```
|
||||
|
||||
## create_user
|
||||
|
||||
Message format:
|
||||
|
@ -58,7 +69,7 @@ Message format:
|
|||
```json
|
||||
{
|
||||
"name": "unique_username",
|
||||
"message": "signed message"
|
||||
"message": "message"
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -1,3 +1,21 @@
|
|||
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() {
|
||||
const sig = await openpgp.sign({
|
||||
message: new openpgp.CleartextMessage("vybe_auth " + rand(), ""),
|
||||
signingKeys: window.keys.priv,
|
||||
});
|
||||
window.socket.emit("authenticate", { name: window.name, message: sig });
|
||||
}
|
||||
|
||||
async function register(e) {
|
||||
e.preventDefault();
|
||||
|
@ -19,11 +37,7 @@ async function message(e) {
|
|||
e.preventDefault();
|
||||
const msg = document.getElementById("msg").value;
|
||||
if (!msg) return;
|
||||
const sig = await openpgp.sign({
|
||||
message: new openpgp.CleartextMessage(msg, ""),
|
||||
signingKeys: window.keys.priv,
|
||||
});
|
||||
window.socket.emit("send_message", { name: window.name, message: sig });
|
||||
window.socket.emit("send_message", { name: window.name, message: msg });
|
||||
document.getElementById("msg").value = "";
|
||||
const el = document.createElement("div");
|
||||
el.classList.add("message");
|
||||
|
@ -40,6 +54,7 @@ async function loadKeys(keys) {
|
|||
const priv = await openpgp.readKey({ armoredKey: keys.privateKey });
|
||||
const pub = await openpgp.readKey({ armoredKey: keys.publicKey });
|
||||
window.keys = { priv, pub };
|
||||
await auth();
|
||||
}
|
||||
|
||||
async function loadMessages() {
|
||||
|
@ -51,7 +66,7 @@ async function loadMessages() {
|
|||
|
||||
window.onload = () => {
|
||||
window.socket = io();
|
||||
window.socket.on("create_user", swap);
|
||||
window.socket.on("create_user", auth);
|
||||
window.socket.on("new_message", (msg) => {
|
||||
const el = document.createElement("div");
|
||||
el.classList.add("message");
|
||||
|
@ -72,10 +87,11 @@ window.onload = () => {
|
|||
}
|
||||
if (!msg.more) document.getElementById("loadmore").classList.add("hidden");
|
||||
});
|
||||
window.socket.on("authenticate", swap);
|
||||
const keys = localStorage.getItem("keys");
|
||||
if (keys) {
|
||||
window.name = localStorage.getItem("name");
|
||||
loadKeys(JSON.parse(keys)).then(swap);
|
||||
loadKeys(JSON.parse(keys)).then(() => {});
|
||||
}
|
||||
document.getElementById("registerform").onsubmit = register;
|
||||
document.getElementById("msginput").onsubmit = message;
|
||||
|
|
|
@ -24,6 +24,9 @@
|
|||
.message {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
#loadmore {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
@ -5,6 +5,12 @@ CREATE TABLE users (
|
|||
created timestamp default current_timestamp
|
||||
);
|
||||
|
||||
CREATE TABLE authentications (
|
||||
user integer,
|
||||
salt text,
|
||||
foreign key(user) references users(id)
|
||||
);
|
||||
|
||||
CREATE TABLE threads (
|
||||
id integer primary key asc,
|
||||
created timestamp default current_timestamp
|
||||
|
@ -15,7 +21,6 @@ CREATE TABLE posts (
|
|||
user integer,
|
||||
thread integer,
|
||||
content text,
|
||||
sig text,
|
||||
created timestamp default current_timestamp,
|
||||
foreign key(user) references users(id),
|
||||
foreign key(thread) references threads(id)
|
||||
|
|
2
index.js
2
index.js
|
@ -25,4 +25,4 @@ server.listen(PORT, () => {
|
|||
console.log("server running on port " + PORT);
|
||||
});
|
||||
|
||||
app.use(express.static('client'));
|
||||
app.use(express.static("client"));
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
const create_user = require("./create_user");
|
||||
const get_history = require("./get_history");
|
||||
const send_message = require("./send_message");
|
||||
const authenticate = require("./authenticate");
|
||||
|
||||
module.exports = {
|
||||
create_user,
|
||||
get_history,
|
||||
send_message,
|
||||
authenticate,
|
||||
};
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
const db = require("../db");
|
||||
const openpgp = require("openpgp");
|
||||
|
||||
const authenticate = async (msg, respond, socket) => {
|
||||
if (!msg.name || !msg.message) {
|
||||
return respond({
|
||||
success: false,
|
||||
message: "Invalid message",
|
||||
});
|
||||
}
|
||||
const result = await db.query("select * from users where name = ?", [
|
||||
msg.name,
|
||||
]);
|
||||
if (result.rows.length === 0) {
|
||||
return respond({
|
||||
success: false,
|
||||
message: "User not found",
|
||||
});
|
||||
}
|
||||
try {
|
||||
const key = await openpgp.readKey({ armoredKey: result.rows[0].pubkey });
|
||||
const verification = await openpgp.verify({
|
||||
message: await openpgp.readCleartextMessage({
|
||||
cleartextMessage: msg.message,
|
||||
}),
|
||||
verificationKeys: key,
|
||||
expectSigned: true,
|
||||
});
|
||||
const data = verification.data.split(" ");
|
||||
if (data[0] !== "vybe_auth") {
|
||||
return respond({
|
||||
success: false,
|
||||
message: "Bad auth message",
|
||||
});
|
||||
}
|
||||
const auths = await db.query(
|
||||
"select * from authentications where user = ? and salt = ?",
|
||||
[result.rows[0].id, data[1]]
|
||||
);
|
||||
if (auths.rows.length === 0) {
|
||||
await db.query("insert into authentications (user, salt) values (?, ?)", [
|
||||
result.rows[0].id,
|
||||
data[1],
|
||||
]);
|
||||
socket.userid = result.rows[0].id;
|
||||
return respond({
|
||||
success: true,
|
||||
});
|
||||
} else {
|
||||
return respond({
|
||||
success: false,
|
||||
message: "Already authenticated with this message",
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
return respond({
|
||||
success: false,
|
||||
message: "Message signature verification failed",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = authenticate;
|
|
@ -0,0 +1,22 @@
|
|||
const db = require("../db");
|
||||
|
||||
const authwrap = (fn) => async (msg, respond, socket) => {
|
||||
if (!socket.userid) {
|
||||
return respond({
|
||||
success: false,
|
||||
message: "Not authenticated",
|
||||
});
|
||||
}
|
||||
const result = await db.query("select * from users where id = ?", [
|
||||
socket.userid,
|
||||
]);
|
||||
if (result.rows.length === 0) {
|
||||
return respond({
|
||||
success: false,
|
||||
message: "User not found",
|
||||
});
|
||||
}
|
||||
return await fn({ ...msg, auth_user: result.rows[0] }, respond, socket);
|
||||
};
|
||||
|
||||
module.exports = authwrap;
|
|
@ -1,47 +1,21 @@
|
|||
const db = require("../db");
|
||||
const openpgp = require("openpgp");
|
||||
const authwrap = require("./authwrap");
|
||||
|
||||
const send_message = async (msg, respond, socket) => {
|
||||
// find user
|
||||
const result = await db.query("select * from users where name = ?", [
|
||||
msg.name,
|
||||
]);
|
||||
if (result.rows.length === 0) {
|
||||
return respond({
|
||||
success: false,
|
||||
message: "User not found",
|
||||
});
|
||||
}
|
||||
// validate signature
|
||||
try {
|
||||
const key = await openpgp.readKey({ armoredKey: result.rows[0].pubkey });
|
||||
const verification = await openpgp.verify({
|
||||
message: await openpgp.readCleartextMessage({
|
||||
cleartextMessage: msg.message,
|
||||
}),
|
||||
verificationKeys: key,
|
||||
expectSigned: true,
|
||||
});
|
||||
// add message and send it to everyone
|
||||
const id = await db.query(
|
||||
"insert into posts (user, thread, content, sig) values (?, 1, ?, ?) returning id",
|
||||
[result.rows[0].id, verification.data, msg.message]
|
||||
"insert into posts (user, thread, content) values (?, 1, ?) returning id",
|
||||
[msg.auth_user.id, msg.message]
|
||||
);
|
||||
socket.broadcast.emit("new_message", {
|
||||
id: id.rows[0].id,
|
||||
name: msg.name,
|
||||
message: verification.data,
|
||||
message: msg.message,
|
||||
});
|
||||
return respond({
|
||||
success: true,
|
||||
id: id.rows[0].id,
|
||||
});
|
||||
} catch (err) {
|
||||
return respond({
|
||||
success: false,
|
||||
message: "Message signature verification failed",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = send_message;
|
||||
module.exports = authwrap(send_message);
|
||||
|
|
Loading…
Reference in New Issue