aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--index.js2
-rw-r--r--lib/register.js121
-rw-r--r--routes/lnurl.js53
-rw-r--r--routes/users.js183
5 files changed, 198 insertions, 162 deletions
diff --git a/.gitignore b/.gitignore
index 130d682f..28a82f66 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,4 @@ config/clientversion
yarn-error.log
.pnp*
.yarn*
+tls.*
diff --git a/index.js b/index.js
index d31cb93b..822076fe 100644
--- a/index.js
+++ b/index.js
@@ -11,6 +11,7 @@ config = require("./config");
networks = [];
prod = process.env.NODE_ENV === "production";
+challenge = {};
logins = {};
sessions = {};
sockets = {};
@@ -38,6 +39,7 @@ require("./lib/sockets");
require("./lib/passport");
require("./lib/rates");
require("./lib/notifications");
+require("./lib/register");
require("./routes/assets");
require("./routes/invoices");
diff --git a/lib/register.js b/lib/register.js
new file mode 100644
index 00000000..bc17496e
--- /dev/null
+++ b/lib/register.js
@@ -0,0 +1,121 @@
+const authenticator = require("otplib").authenticator;
+const axios = require("axios");
+const bcrypt = require("bcrypt");
+
+const DAY = 24 * 60 * 60 * 1000;
+
+let faucet = 1000;
+setInterval(() => (faucet = 2000), DAY);
+
+const gift = async user => {
+ const account = user.accounts[0];
+
+ if (faucet > 0) {
+ faucet -= 100;
+
+ account.balance = 100;
+ await account.save();
+
+ const payment = await db.Payment.create({
+ account_id: account.id,
+ user_id: user.id,
+ hash: "Welcome Gift",
+ amount: 100,
+ currency: user.currency,
+ rate: app.get("rates")[user.currency],
+ received: true,
+ confirmed: 1,
+ network: "GIFT"
+ });
+
+ await user.save();
+ }
+};
+
+register = async (user, ip, requireChallenge) => {
+ if (!(user && user.username)) throw new Error("Username required");
+
+ let exists = await db.User.count({ where: { username: user.username } });
+ if (exists) throw new Error(`Username ${user.username} taken`);
+
+ if (user.password && user.password === user.confirm) {
+ user.password = await bcrypt.hash(user.password, 1);
+ }
+
+ if (config.bitcoin) {
+ if (config.bitcoin.walletpass)
+ await bc.walletPassphrase(config.bitcoin.walletpass, 300);
+
+ user.address = await bc.getNewAddress("", "bech32");
+ addresses[user.address] = user.username;
+ }
+
+ if (config.liquid) {
+ if (config.liquid.walletpass)
+ await lq.walletPassphrase(config.liquid.walletpass, 300);
+
+ user.confidential = await lq.getNewAddress();
+ user.liquid = (await lq.getAddressInfo(user.confidential)).unconfidential;
+ addresses[user.liquid] = user.username;
+ }
+
+ if (
+ requireChallenge &&
+ (!challenge[ip] ||
+ user.response.toLowerCase() !== challenge[ip].toLowerCase())
+ ) {
+ l.info("failed challenge", ip, user.response, challenge[ip]);
+ throw new Error("Failed challenge");
+ }
+
+ delete challenge[ip];
+
+ let countries = {
+ CA: "CAD",
+ US: "USD",
+ JP: "JPY",
+ CN: "CNY",
+ AU: "AUD",
+ GB: "GBP",
+ };
+
+ if (!config.ipstack || ip.startsWith("127") || ip.startsWith("192"))
+ user.currency = "CAD";
+ else {
+ let info = await axios.get(
+ `http://api.ipstack.com/${ip}?access_key=${config.ipstack}`
+ );
+ user.currency = countries[info.data.country_code] || "USD";
+ }
+
+ user.currencies = [...new Set([user.currency, "CAD", "USD", "JPY"])];
+ user.fiat = false;
+ user.otpsecret = authenticator.generateSecret();
+
+ user = await db.User.create(user);
+
+ let account = await db.Account.create({
+ user_id: user.id,
+ asset: config.liquid.btcasset,
+ balance: 0,
+ pending: 0,
+ name: "Bitcoin",
+ ticker: "BTC",
+ precision: 8,
+ });
+
+ user.accounts = [account];
+
+ const d = ip.split(".");
+ const numericIp = ((+d[0] * 256 + +d[1]) * 256 + +d[2]) * 256 + +d[3];
+ if (Number.isInteger(numericIp)) {
+ user.ip = numericIp;
+ const ipExists = await db.User.findOne({ where: { ip: numericIp } });
+ if (!ipExists) await gift(user);
+ }
+
+ user.account_id = account.id;
+ await user.save();
+ l.info("new user", user.username, ip);
+ return user;
+};
diff --git a/routes/lnurl.js b/routes/lnurl.js
index fad15ffe..05308d41 100644
--- a/routes/lnurl.js
+++ b/routes/lnurl.js
@@ -27,7 +27,7 @@ app.get("/withdraw", auth, async (req, res) => {
res.send(result);
} catch (e) {
- l.error(e.message);
+ l.error("problem generating withdrawl url", e.message);
res.status(500).send(e.message);
}
});
@@ -71,7 +71,7 @@ app.get("/login", optionalAuth, async (req, res) => {
res.send(result);
} catch (e) {
- l.error(e.message);
+ l.error("problem generating login url", e.message);
res.status(500).send(e.message);
}
});
@@ -84,7 +84,7 @@ app.get("/decode", async (req, res) => {
let result = await axios.get(decoded);
res.send(result.data);
} catch (e) {
- l.error(e.message);
+ l.error("problem decoding lnurl", e.message);
res.status(500).send(e.message);
}
});
@@ -96,20 +96,29 @@ lnurlServer.bindToHook(
const { key, tag, pr, k1 } = req.query;
if (tag === "login") {
- const username = logins[k1];
+ let username = logins[k1];
+
if (!username) {
- user = await db.Key.findOne({
+ user = (await db.Key.findOne({
where: { hex: key },
- });
+ include: [{
+ model: db.User,
+ as: 'user'
+ }],
+ })).user;
if (!user) {
- next(new Error("User not found"));
+ let ip = req.headers["x-forwarded-for"] || req.connection.remoteAddress;
+ user = await register({
+ username: key.substr(0, 8),
+ password: key,
+ }, ip);
}
+
+ ({ username } = user);
}
logins[key] = { k1, username };
-
- return next();
}
if (pr) {
@@ -197,6 +206,7 @@ lnurlServer.bindToHook(
);
lnurlServer.bindToHook("login", async (key) => {
+ l.info("logging in");
try {
const exists = await db.Key.findOne({
where: { hex: key },
@@ -204,21 +214,28 @@ lnurlServer.bindToHook("login", async (key) => {
});
let user;
- if (logins[key]) {
+ if (logins[key] && logins[key] !== "undefined") {
const { username } = logins[key];
user = await db.User.findOne({
where: { username },
});
- const k = await db.Key.create({
- user_id: user.id,
- hex: key,
- });
+ if (user) {
+ const k = await db.Key.findOrCreate({
+ user_id: user.id,
+ hex: key,
+ });
- l.info("added key", username, k);
- emit(username, "key", k);
+ l.info("added key", username, k);
+ emit(username, "key", k);
+ } else {
+ l.info("user not found");
+ user = await register({
+ username: key,
+ password: key,
+ });
+ }
} else if (exists) ({ user } = exists);
- else return;
if (user && user.username) {
const payload = { username: user.username };
@@ -228,6 +245,6 @@ lnurlServer.bindToHook("login", async (key) => {
ws.send(JSON.stringify({ type: "token", data: token }));
}
} catch (e) {
- l.error(e.message);
+ l.error("problem with login hook", e.message);
}
});
diff --git a/routes/users.js b/routes/users.js
index 02e76b0b..a8b92860 100644
--- a/routes/users.js
+++ b/routes/users.js
@@ -6,16 +6,12 @@ const textToImage = require("text-to-image");
const randomWord = require("random-words");
const pick = (O, ...K) => K.reduce((o, k) => ((o[k] = O[k]), o), {});
-let faucet = 1000;
-const DAY = 24 * 60 * 60 * 1000;
require("../lib/whitelist");
-const challenge = {};
-
const twofa = (req, res, next) => {
let {
user,
- body: { token }
+ body: { token },
} = req;
if (
user.twofa &&
@@ -26,36 +22,11 @@ const twofa = (req, res, next) => {
} else next();
};
-const gift = async user => {
- const account = user.accounts[0];
-
- if (faucet > 0) {
- faucet -= 100;
-
- account.balance = 100;
- await account.save();
-
- const payment = await db.Payment.create({
- account_id: account.id,
- user_id: user.id,
- hash: "Welcome Gift",
- amount: 100,
- currency: user.currency,
- rate: app.get("rates")[user.currency],
- received: true,
- confirmed: 1,
- network: "GIFT"
- });
-
- await user.save();
- }
-};
-
app.get("/users/:username", async (req, res) => {
const { username } = req.params;
const user = await db.User.findOne({
attributes: ["username"],
- where: { username }
+ where: { username },
});
if (user) res.send(user);
else res.status(500).send("User not found");
@@ -70,94 +41,13 @@ app.get("/challenge", async (req, res) => {
});
app.post("/register", async (req, res) => {
- let err = m => res.status(500).send(m);
- let { user } = req.body;
-
- if (!(user && user.username)) return err("Username required");
-
- let exists = await db.User.count({ where: { username: user.username } });
- if (exists) return err("Username taken");
-
- if (user.password && user.password === user.confirm) {
- user.password = await bcrypt.hash(user.password, 1);
- }
-
- if (config.bitcoin) {
- if (config.bitcoin.walletpass)
- await bc.walletPassphrase(config.bitcoin.walletpass, 300);
-
- user.address = await bc.getNewAddress("", "bech32");
- addresses[user.address] = user.username;
- }
-
- if (config.liquid) {
- if (config.liquid.walletpass)
- await lq.walletPassphrase(config.liquid.walletpass, 300);
-
- user.confidential = await lq.getNewAddress();
- user.liquid = (await lq.getAddressInfo(user.confidential)).unconfidential;
- addresses[user.liquid] = user.username;
- }
-
- let ip = req.headers["x-forwarded-for"] || req.connection.remoteAddress;
-
- if (
- !challenge[ip] ||
- user.response.toLowerCase() !== challenge[ip].toLowerCase()
- ) {
- l.info("failed challenge", ip, user.response, challenge[ip]);
- return res.status(500).send("Failed challenge");
- }
-
- delete challenge[ip];
-
- let countries = {
- CA: "CAD",
- US: "USD",
- JP: "JPY",
- CN: "CNY",
- AU: "AUD",
- GB: "GBP"
- };
-
- if (!config.ipstack || ip.startsWith("127") || ip.startsWith("192"))
- user.currency = "CAD";
- else {
- let info = await axios.get(
- `http://api.ipstack.com/${ip}?access_key=${config.ipstack}`
- );
- user.currency = countries[info.data.country_code] || "USD";
- }
-
- user.currencies = [...new Set([user.currency, "CAD", "USD", "JPY"])];
- user.otpsecret = authenticator.generateSecret();
-
- user = await db.User.create(user);
-
- let account = await db.Account.create({
- user_id: user.id,
- asset: config.liquid.btcasset,
- balance: 0,
- pending: 0,
- name: "Bitcoin",
- ticker: "BTC",
- precision: 8
- });
-
- user.accounts = [account];
-
- const d = ip.split(".");
- const numericIp = ((+d[0] * 256 + +d[1]) * 256 + +d[2]) * 256 + +d[3];
- if (Number.isInteger(numericIp)) {
- user.ip = numericIp;
- const ipExists = await db.User.findOne({ where: { ip: numericIp } });
- if (!ipExists) await gift(user);
+ try {
+ const ip = req.headers["x-forwarded-for"] || req.connection.remoteAddress;
+ const user = await register(req.body.user, ip, true);
+ res.send(pick(user, ...whitelist));
+ } catch (e) {
+ res.status(500).send(e.message);
}
-
- user.account_id = account.id;
- await user.save();
- res.send(pick(user, ...whitelist));
- l.info("new user", user.username, ip);
});
app.post("/disable2fa", auth, twofa, async (req, res) => {
@@ -209,11 +99,11 @@ app.post("/user", auth, async (req, res) => {
password,
passconfirm,
tokens,
- seed
+ seed,
} = req.body;
let exists = await db.User.findOne({
- where: { username }
+ where: { username },
});
let token;
@@ -229,7 +119,7 @@ app.post("/user", auth, async (req, res) => {
token = jwt.sign({ username }, config.jwt);
res.cookie("token", token, {
- expires: new Date(Date.now() + 432000000)
+ expires: new Date(Date.now() + 432000000),
});
}
@@ -259,25 +149,25 @@ app.post("/keys", auth, async (req, res) => {
const { key: hex } = req.body;
const key = await db.Key.create({
user_id: req.user.id,
- hex
+ hex,
});
emit(req.user.username, "key", key);
});
app.post("/keys/delete", auth, async (req, res) => {
const { hex } = req.body;
- (await db.Key.findOne({ where: {
- user_id: req.user.id,
- hex
- }})).destroy();
+ (
+ await db.Key.findOne({
+ where: {
+ user_id: req.user.id,
+ hex,
+ },
+ })
+ ).destroy();
});
app.post("/login", async (req, res) => {
- const {
- params,
- sig,
- key
- } = req.body;
+ const { params, sig, key } = req.body;
if (sig) {
const { callback } = params;
@@ -287,7 +177,7 @@ app.post("/login", async (req, res) => {
const response = await axios.get(url);
res.send(response.data);
} catch (e) {
- l.error(e.message);
+ l.error("problem calling lnurl login", e.message);
res.status(500).send(e.message);
}
@@ -339,15 +229,20 @@ app.post("/login", async (req, res) => {
app.post("/logout", auth, async (req, res) => {
let { subscription } = req.body;
- l.info("logging out", req.user.username);
+ const { username } = req.user;
+ l.info("logging out", username);
if (!subscription) return res.end();
let i = req.user.subscriptions.findIndex(
- s => JSON.stringify(s) === subscription
+ (s) => JSON.stringify(s) === subscription
);
if (i > -1) {
req.user.subscriptions.splice(i, 1);
}
await req.user.save();
+ Object.keys(logins).map(
+ (k) => logins[k]["username"] === username && delete logins[k]
+ );
+
res.end();
});
@@ -373,7 +268,7 @@ app.post("/account", auth, async (req, res) => {
try {
const account = await db.Account.findOne({
- where: { user_id, asset }
+ where: { user_id, asset },
});
account.name = name;
@@ -396,21 +291,21 @@ app.post("/shiftAccount", auth, async (req, res) => {
try {
const account = await db.Account.findOne({
- where: { user_id: user.id, asset }
+ where: { user_id: user.id, asset },
});
user.account_id = account.id;
await user.save();
let payments = await user.getPayments({
where: {
- account_id: user.account_id
+ account_id: user.account_id,
},
order: [["id", "DESC"]],
limit: 12,
include: {
model: db.Account,
- as: "account"
- }
+ as: "account",
+ },
});
user.payments = payments;
user.account = account;
@@ -423,18 +318,18 @@ app.post("/shiftAccount", auth, async (req, res) => {
}
});
-setInterval(() => (faucet = 2000), DAY);
-
-app.get("/vapidPublicKey", function(req, res) {
+app.get("/vapidPublicKey", function (req, res) {
res.send(config.vapid.publicKey);
});
-app.post("/subscribe", auth, async function(req, res) {
+app.post("/subscribe", auth, async function (req, res) {
let { subscriptions } = req.user;
let { subscription } = req.body;
if (!subscriptions) subscriptions = [];
if (
- !subscriptions.find(s => JSON.stringify(s) === JSON.stringify(subscription))
+ !subscriptions.find(
+ (s) => JSON.stringify(s) === JSON.stringify(subscription)
+ )
)
subscriptions.push(subscription);
req.user.subscriptions = subscriptions;