Switch to json web tokens.

This commit is contained in:
Mike Cao 2020-07-22 20:45:09 -07:00
parent 5219582803
commit cb0c912c5b
10 changed files with 202 additions and 86 deletions

View file

@ -1,31 +1,38 @@
import crypto from 'crypto';
import { v5 as uuid, v4 } from 'uuid';
import Cryptr from 'cryptr';
import { v5 } from 'uuid';
import jwt from 'jsonwebtoken';
import bcrypt from 'bcrypt';
const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/;
const cryptr = new Cryptr(hash(process.env.HASH_SALT, process.env.DATABASE_URL));
export function md5(s) {
return crypto.createHash('md5').update(s).digest('hex');
export function sha256(...args) {
return crypto.createHash('sha256').update(args.join('')).digest('hex');
}
export function hash(...args) {
return uuid(args.join(''), md5(process.env.HASH_SALT));
export function secret() {
return sha256(process.env.HASH_SALT);
}
export function validHash(s) {
export function uuid(...args) {
return v5(args.join(''), v5(process.env.HASH_SALT, v5.DNS));
}
export function random(n = 64) {
return crypto.randomBytes(n).toString('hex');
}
export function isValidHash(s) {
return UUID_REGEX.test(s);
}
export function encrypt(s) {
return cryptr.encrypt(s);
export async function createToken(payload, options) {
return jwt.sign(payload, secret(), options);
}
export function decrypt(s) {
return cryptr.decrypt(s);
export async function parseToken(token, options) {
return jwt.verify(token, secret(), options);
}
export function random() {
return v4();
export function checkPassword(password, hash) {
return bcrypt.compare(password, hash);
}

View file

@ -95,3 +95,13 @@ export async function saveEvent(website_id, session_id, url, event_type, event_v
}),
);
}
export async function getAccount(username = '') {
return runQuery(
prisma.account.findOne({
where: {
username,
},
}),
);
}

View file

@ -1,50 +1,52 @@
import { getWebsite, getSession, createSession } from 'lib/db';
import { getCountry, getDevice, getIpAddress, isValidSession } from 'lib/utils';
import { hash } from 'lib/crypto';
import { getCountry, getDevice, getIpAddress } from 'lib/utils';
import { uuid, parseToken, isValidHash } from 'lib/crypto';
export default async req => {
const { payload } = req.body;
const { session } = payload;
const { website: website_uuid, hostname, screen, language, session } = payload;
if (isValidSession(session)) {
return session;
if (!isValidHash(website_uuid)) {
throw new Error(`Invalid website: ${website_uuid}`);
}
const ip = getIpAddress(req);
const { userAgent, browser, os } = getDevice(req);
const country = await getCountry(req, ip);
const { website: website_uuid, hostname, screen, language } = payload;
try {
return await parseToken(session);
} catch {
const ip = getIpAddress(req);
const { userAgent, browser, os } = getDevice(req);
const country = await getCountry(req, ip);
if (website_uuid) {
const website = await getWebsite(website_uuid);
if (website_uuid) {
const website = await getWebsite(website_uuid);
if (website) {
const { website_id } = website;
const session_uuid = hash(website_id, hostname, ip, userAgent, os);
if (website) {
const { website_id } = website;
const session_uuid = uuid(website_id, hostname, ip, userAgent, os);
let session = await getSession(session_uuid);
let session = await getSession(session_uuid);
if (!session) {
session = await createSession(website_id, {
if (!session) {
session = await createSession(website_id, {
session_uuid,
hostname,
browser,
os,
screen,
language,
country,
});
}
const { session_id } = session;
return {
website_id,
website_uuid,
session_id,
session_uuid,
hostname,
browser,
os,
screen,
language,
country,
});
};
}
const { session_id } = session;
return [
website_id,
website_uuid,
session_id,
session_uuid,
hash(website_id, website_uuid, session_id, session_uuid),
].join(':');
}
}
};

View file

@ -1,9 +1,8 @@
import requestIp from 'request-ip';
import { browserName, detectOS } from 'detect-browser';
import isLocalhost from 'is-localhost-ip';
import maxmind from 'maxmind';
import geolite2 from 'geolite2-redist';
import isLocalhost from 'is-localhost-ip';
import { hash } from './crypto';
export function getIpAddress(req) {
// Cloudflare
@ -44,20 +43,3 @@ export async function getCountry(req, ip) {
return result.country.iso_code;
}
export function parseSession(session) {
const [website_id, website_uuid, session_id, session_uuid, sig] = (session || '').split(':');
return {
website_id: parseInt(website_id),
website_uuid,
session_id: parseInt(session_id),
session_uuid,
sig,
};
}
export function isValidSession(session) {
const { website_id, website_uuid, session_id, session_uuid, sig } = parseSession(session);
return hash(website_id, website_uuid, session_id, session_uuid) === sig;
}