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
|
## create_user
|
||||||
|
|
||||||
Message format:
|
Message format:
|
||||||
|
@ -58,7 +69,7 @@ Message format:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"name": "unique_username",
|
"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) {
|
async function register(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
@ -19,11 +37,7 @@ async function message(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const msg = document.getElementById("msg").value;
|
const msg = document.getElementById("msg").value;
|
||||||
if (!msg) return;
|
if (!msg) return;
|
||||||
const sig = await openpgp.sign({
|
window.socket.emit("send_message", { name: window.name, message: msg });
|
||||||
message: new openpgp.CleartextMessage(msg, ""),
|
|
||||||
signingKeys: window.keys.priv,
|
|
||||||
});
|
|
||||||
window.socket.emit("send_message", { name: window.name, message: sig });
|
|
||||||
document.getElementById("msg").value = "";
|
document.getElementById("msg").value = "";
|
||||||
const el = document.createElement("div");
|
const el = document.createElement("div");
|
||||||
el.classList.add("message");
|
el.classList.add("message");
|
||||||
|
@ -40,6 +54,7 @@ async function loadKeys(keys) {
|
||||||
const priv = await openpgp.readKey({ armoredKey: keys.privateKey });
|
const priv = await openpgp.readKey({ armoredKey: keys.privateKey });
|
||||||
const pub = await openpgp.readKey({ armoredKey: keys.publicKey });
|
const pub = await openpgp.readKey({ armoredKey: keys.publicKey });
|
||||||
window.keys = { priv, pub };
|
window.keys = { priv, pub };
|
||||||
|
await auth();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadMessages() {
|
async function loadMessages() {
|
||||||
|
@ -51,7 +66,7 @@ async function loadMessages() {
|
||||||
|
|
||||||
window.onload = () => {
|
window.onload = () => {
|
||||||
window.socket = io();
|
window.socket = io();
|
||||||
window.socket.on("create_user", swap);
|
window.socket.on("create_user", auth);
|
||||||
window.socket.on("new_message", (msg) => {
|
window.socket.on("new_message", (msg) => {
|
||||||
const el = document.createElement("div");
|
const el = document.createElement("div");
|
||||||
el.classList.add("message");
|
el.classList.add("message");
|
||||||
|
@ -72,10 +87,11 @@ window.onload = () => {
|
||||||
}
|
}
|
||||||
if (!msg.more) document.getElementById("loadmore").classList.add("hidden");
|
if (!msg.more) document.getElementById("loadmore").classList.add("hidden");
|
||||||
});
|
});
|
||||||
|
window.socket.on("authenticate", swap);
|
||||||
const keys = localStorage.getItem("keys");
|
const keys = localStorage.getItem("keys");
|
||||||
if (keys) {
|
if (keys) {
|
||||||
window.name = localStorage.getItem("name");
|
window.name = localStorage.getItem("name");
|
||||||
loadKeys(JSON.parse(keys)).then(swap);
|
loadKeys(JSON.parse(keys)).then(() => {});
|
||||||
}
|
}
|
||||||
document.getElementById("registerform").onsubmit = register;
|
document.getElementById("registerform").onsubmit = register;
|
||||||
document.getElementById("msginput").onsubmit = message;
|
document.getElementById("msginput").onsubmit = message;
|
||||||
|
|
|
@ -24,6 +24,9 @@
|
||||||
.message {
|
.message {
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
#loadmore {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
|
@ -5,6 +5,12 @@ CREATE TABLE users (
|
||||||
created timestamp default current_timestamp
|
created timestamp default current_timestamp
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE TABLE authentications (
|
||||||
|
user integer,
|
||||||
|
salt text,
|
||||||
|
foreign key(user) references users(id)
|
||||||
|
);
|
||||||
|
|
||||||
CREATE TABLE threads (
|
CREATE TABLE threads (
|
||||||
id integer primary key asc,
|
id integer primary key asc,
|
||||||
created timestamp default current_timestamp
|
created timestamp default current_timestamp
|
||||||
|
@ -15,7 +21,6 @@ CREATE TABLE posts (
|
||||||
user integer,
|
user integer,
|
||||||
thread integer,
|
thread integer,
|
||||||
content text,
|
content text,
|
||||||
sig text,
|
|
||||||
created timestamp default current_timestamp,
|
created timestamp default current_timestamp,
|
||||||
foreign key(user) references users(id),
|
foreign key(user) references users(id),
|
||||||
foreign key(thread) references threads(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);
|
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 create_user = require("./create_user");
|
||||||
const get_history = require("./get_history");
|
const get_history = require("./get_history");
|
||||||
const send_message = require("./send_message");
|
const send_message = require("./send_message");
|
||||||
|
const authenticate = require("./authenticate");
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
create_user,
|
create_user,
|
||||||
get_history,
|
get_history,
|
||||||
send_message,
|
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 db = require("../db");
|
||||||
const openpgp = require("openpgp");
|
const authwrap = require("./authwrap");
|
||||||
|
|
||||||
const send_message = async (msg, respond, socket) => {
|
const send_message = async (msg, respond, socket) => {
|
||||||
// find user
|
// add message and send it to everyone
|
||||||
const result = await db.query("select * from users where name = ?", [
|
const id = await db.query(
|
||||||
msg.name,
|
"insert into posts (user, thread, content) values (?, 1, ?) returning id",
|
||||||
]);
|
[msg.auth_user.id, msg.message]
|
||||||
if (result.rows.length === 0) {
|
);
|
||||||
return respond({
|
socket.broadcast.emit("new_message", {
|
||||||
success: false,
|
id: id.rows[0].id,
|
||||||
message: "User not found",
|
name: msg.name,
|
||||||
});
|
message: msg.message,
|
||||||
}
|
});
|
||||||
// validate signature
|
return respond({
|
||||||
try {
|
success: true,
|
||||||
const key = await openpgp.readKey({ armoredKey: result.rows[0].pubkey });
|
id: id.rows[0].id,
|
||||||
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]
|
|
||||||
);
|
|
||||||
socket.broadcast.emit("new_message", {
|
|
||||||
id: id.rows[0].id,
|
|
||||||
name: msg.name,
|
|
||||||
message: verification.data,
|
|
||||||
});
|
|
||||||
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